5.4 KiB
5.4 KiB
Outbox Transport-Only Cutover Implementation Plan
Goal: Preserve canonical integration-event bytes and metadata inside the existing legacy outbox_event authority, then route canonical claimed rows through the messaging platform while legacy rows keep the current broker path.
Spec: docs/superpowers/specs/2026-09-18-outbox-transport-only-cutover-design.md
Constraints
- Preserve all existing uncommitted work. No reset/checkout/stage/commit/amend/push.
- Do not activate or switch to
POLLING_V2. - Do not synthesize missing canonical metadata for legacy rows.
- Do not re-encode a persisted canonical envelope.
- One claimed row goes through exactly one publish branch.
- Default configuration remains legacy-compatible and canonical transport is off.
Task 1 — Split canonical and legacy append ports
- Create
LegacyOutboxAppendPortwith the currentNewOutboxEventsignature. - Change
OutboxAppendPortto acceptValidatedIntegrationEvent. - Move all current raw production consumers and their tests to
LegacyOutboxAppendPort. - Make
OutboxStoreAdapterimplementLegacyOutboxAppendPortonly. - Run
:application-core:testand focused sample/outbox compile tests.
Task 2 — Add additive canonical columns to outbox_event
- Add the next PostgreSQL migration after V12.
- Widen event/correlation identifiers as required.
- Add canonical metadata, exact
BYTEA, hashes/revisions, and all-or-none check constraints. - Extend
OutboxEventEntitymappings. - Update migration history expectations.
- Add real PostgreSQL integration assertions for legacy rows and canonical shape constraints.
Task 3 — Implement canonical append adapter
- Add
CanonicalOutboxAppendAdapter. - Strictly validate UTF-8 compatibility projection.
- Persist every canonical field and exact
envelopeBytes. - Preserve old required columns for legacy relay/storage compatibility.
- Add unit tests for byte equality, field mapping and invalid UTF-8.
- Gate bean exposure on
ca-skeleton.outbox.canonical-transport-enabled=true.
Task 4 — Split the claimed row model
- Add sealed
ClaimedOutboxEvent. - Keep
OutboxEventas legacy subtype. - Add
CanonicalClaimedOutboxEventcarrying reconstructedValidatedIntegrationEvent. - Change
OutboxStorePort.claimBatchto return the sealed type. - Map all-canonical rows to canonical subtype and all-null rows to legacy subtype.
- Reject partial canonical rows.
- Update legacy relay tests without changing its state-machine semantics.
Task 5 — Route canonical claims through the platform
- Update
OutboxMessagePublishPortto acceptClaimedOutboxEvent. - Extend
OutboxMessagePublishAdapterwith canonicalIntegrationEventPublishPort. - Legacy subtype uses only
MessageBroker. - Canonical subtype uses only the application canonical publish port and exact stored bytes.
- Add focused branch-isolation and outcome tests.
Task 6 — Add explicit activation and composition validation
- Add
canonicalTransportEnabledtoOutboxSettingsandconfig/outbox.yml, default false. - Startup fails when canonical transport is enabled but no
IntegrationEventPublishPortexists. - Relay-enabled compatibility deployment still requires the legacy broker until a later zero-legacy-backlog proof.
- Default-off composition keeps the existing legacy path.
- Enabled composition exposes the canonical append/publish path without a second scheduler.
- Update configuration docs/SSOT.
Task 7 — Regression and architecture verification
:application-core:check:adapter:outbound:persistence-jpa:check- focused PostgreSQL migration/outbox integration lane
:adapter:outbound:messaging:check:sample-portfolio:check— blocked by pre-existingJpaLiveEventReplayAdaptermissingDurationwiring; the same 3SampleApplicationContextTestfailures reproduce on clean HEAD.- focused sample outbox regression tests (
PosterEventPublisherTest,CreateWorkLogOutboxTest,WorkLogUseCasesTest,WorkLogAuthorizationContractTest) :app-bootstrap:architectureTest :app-bootstrap:systemTestverifyCleanArchitectureDependencies:app-bootstrap:verifyEnvKeys- static scans: no platform runtime/Kafka import in canonical bridge
git diff --check- no separate LLM Wiki branch-note warranted; spec, plan, module README/CLAUDE and configuration reference carry the implementation decision.
Verification result
Transport-only cutover implementation is complete for this slice.
Passed:
:application-core:check:adapter:outbound:persistence-jpa:check:adapter:outbound:persistence-jpa:postgresqlMigrationIntegrationTest:adapter:outbound:messaging:check- focused sample outbox regression tests
:app-bootstrap:test- focused
:app-bootstrap:integrationTestoutbox append + row-lifecycle contracts :app-bootstrap:architectureTest:app-bootstrap:systemTestverifyCleanArchitectureDependencies:app-bootstrap:verifyEnvKeysgit diff --check
Known unrelated blocker:
- full
:sample-portfolio:checkstill fails only the 3 previously documentedSampleApplicationContextTestcases becauseJpaLiveEventReplayAdapterrequires an unboundDurationbean. This reproduces on clean HEAD and was not introduced by this cutover.
Publication authority remains LEGACY_POLLING; no code path in this slice activates POLLING_V2.