refactor: 각 어댑터터별 리펙토링 진행
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
# P1 remediation status — the five module reviews
|
||||
|
||||
**Reviews:** `docs/reviews/2026-08-14-{jpa,graphql,messaging,mongodb,notification}-module-code-review.md`
|
||||
**Baseline:** the P0 pass was already complete when this pass began; this file records the P1 pass,
|
||||
which is complete — all 31 findings closed, two of them by establishing that the review's own
|
||||
accepted outcome was already met rather than by writing code.
|
||||
**Verified at:** repo-wide `test`, `spotlessCheck`, `verifyCleanArchitectureDependencies`,
|
||||
`verifyEnvKeys`, `verifyPublicPathSnapshot` and the root `CleanArchitectureTest` all green.
|
||||
|
||||
This file exists because the status was previously carried only in conversation and had to be
|
||||
reconstructed. A finding's row is the claim; the evidence column is where the claim is falsifiable.
|
||||
|
||||
## The recurring defect
|
||||
|
||||
Nearly every P1 in these five reviews is one shape: **a control that exists, passes its own tests,
|
||||
and is reached by nothing.** Not a wrong algorithm — an unreachable one. The tests passed because
|
||||
they constructed the class directly; the capability was absent because no configuration could.
|
||||
|
||||
Examples closed in this pass: the Mongo typed-update path (`MongoBulkExecutor` and
|
||||
`MongoAtomicOperationsTemplate` were constructed by nothing), the entire Mongo change-stream
|
||||
capability (no production code opened a stream at all), `GraphQlBatchLoaderRegistrar.register` (no
|
||||
caller, so the wrong-key refusal never ran on an executing query), `PublishOptions.timeout()` (read
|
||||
by nobody on the real publish path), `markExhausted` (no caller, so a row that spent its budget
|
||||
stayed `AMBIGUOUS` forever), `OutboxMessagePublishPort.publishForOutcome` (no caller, so every
|
||||
ambiguous publish collapsed into an exception), the outbox relay itself (no bean ran a pass), and
|
||||
`BackpressureController` (a limiter the publish path never consulted, reporting `globalInFlight: 0`
|
||||
under any load).
|
||||
|
||||
The lesson worth keeping: **a passing unit test is not evidence a capability exists.** The
|
||||
reachability question — what constructs this, and on which request path — has to be asked
|
||||
separately, and several of the tests added in this pass exist only to ask it.
|
||||
|
||||
## Status
|
||||
|
||||
| Finding | Verdict | Evidence |
|
||||
| --- | --- | --- |
|
||||
| JPA-005 | closed already | roll-up in `NotificationRequestStatusPolicy`; port is tenant-scoped; ArchUnit `PERSISTENCE_DOES_NOT_DEPEND_ON_APPLICATION_SERVICES` |
|
||||
| JPA-006 | closed already | `PersistenceJpaRootAutoConfiguration` is in `AutoConfiguration.imports` and imports the real runtime config |
|
||||
| JPA-007 | closed under item 6 | the review offers two accepted outcomes: full integration, or the interim state under item 6's two conditions. Both hold and were verified in code — `FullTransactionRetryCoordinator:89-93` resolves the policy per call from the calling profile, and `DefaultJpaRetryPolicy:61-64` returns on `!failure.retryable()` before consulting the category allowlist |
|
||||
| JPA-008 | closed | nothing registered a `VendorFailureTranslator`, so every executor ran `withoutCatalogs()` and a 40001 never reached the retry classifier |
|
||||
| JPA-026 | closed | a rehydrated callback event had no matcher once `attempt_id` was null; hash fallback + write-back added |
|
||||
| JPA-028 | closed | the reaper query had no caller, uploads had no terminal state, cleanup decided from a lease it had read rather than claiming |
|
||||
| JPA-029 | closed already | tuple cutoff implemented; the signal is documented best-effort by decision |
|
||||
| GQL-004 | not a defect | every evidence bullet false at HEAD; no WebFlux dependency exists, transport disagreement fails startup |
|
||||
| GQL-011 | closed | unkeyed truncated SHA-256 over actor/tenant replaced with a keyed, rotating HMAC; no default key |
|
||||
| GQL-015 | closed | schema extensions were invisible to the comparator, so a field removed by `extend type` produced no change at all |
|
||||
| GQL-016 | closed | the batch executor returned the loader's map verbatim, so the wrong-key refusal and missing-key policy never ran |
|
||||
| GQL-017 | closed | the blocking bridge was opt-in and null by default, so a reactive runtime ran blocking chunks on the event loop |
|
||||
| MNG-010 | closed | the guardrail was a `Set<String>` asserted against itself; now an ArchUnit rule over the real production graph at the composition root |
|
||||
| MNG-012 | closed | a failed abort or close on a committed transaction was discarded by a closing brace |
|
||||
| MNG-018 | closed | TLS and auth were asserted against a settings object; four TLS cases now run against real servers |
|
||||
| MNG-024 | closed | the reactive binder carried read preference and write concern only, so reactive writes skipped auditing and callbacks |
|
||||
| MNG-026 | closed | both typed-update paths were unreachable, and the bulk executor could be built with no policy at all |
|
||||
| MNG-028 | closed | no production code opened a change stream; the consumer now owns load → resume → stream → project → checkpoint |
|
||||
| MSG-006 | closed | `markExhausted` had no caller and the scheduler's backoff was never written; a relay worker now runs passes |
|
||||
| MSG-008 | closed | validators were beans nothing injected, and the documented configuration bound nowhere; destination/broker/security sections now bind under `app.messaging` and the reference document is executed by a test |
|
||||
| MSG-010 | closed | `BackpressureController` deleted as an unreachable duplicate; its one unique capability moved into the gate that is called |
|
||||
| MSG-012 | closed | header values accepted CR/LF/NUL, identifiers were bounded in chars not bytes, `traceparent` was any string, denylists matched exact spellings only |
|
||||
| MSG-014 | closed | `hasLiveBrokerCertification` is derived from recorded evidence rather than declared, and the evidence is now a manifest a fault lane wrote against a real broker rather than a hand-authored list |
|
||||
| MSG-015 | semantic half closed | the outcome-aware publish path is wired; the anti-corruption bridge needs a `modules.json` edge and is an architecture decision |
|
||||
| MSG-016 | closed | no reserved name existed for tenant, so every consumed message was rebuilt with none; the canonical metadata is now columns, and the CDC event key moved off `destination`, which had put every message on a topic onto one partition |
|
||||
| MSG-017 | closed | the timeout is an absolute deadline; contradictory `PublishResult` combinations are unrepresentable; `brokerHints` removed |
|
||||
| NTF-015 | closed | webhook signing was one shared secret for every subscription; SES silently dropped attachments and now sends them as raw MIME |
|
||||
| NTF-016 | closed | retired keys were forced to one purpose so a provider-credential drain failed; required purposes now follow enabled capabilities |
|
||||
| NTF-019 | closed already | split inbound ports carry capabilities; four ArchUnit rules with negative fixtures close the gate |
|
||||
|
||||
## Found while closing, not in any review
|
||||
|
||||
`SmtpMimeMessageFactory` handed JavaMail the resolver's one-shot stream. JavaMail reads an
|
||||
attachment twice — once to choose the part's transfer encoding, once to write it — so the second
|
||||
read returned nothing and the message went out announcing a filename and carrying no bytes, with
|
||||
the attempt recorded as accepted. Every existing test asserted on the outcome of the send rather
|
||||
than on what was sent, which is why a bug that emptied every attachment on the one provider family
|
||||
this platform can actually assemble survived a full review pass.
|
||||
|
||||
`SmtpAttachmentBodyTest` now reads the attachment back off the serialised message the way a
|
||||
receiving client would. It was confirmed to fail against the original code and pass against the
|
||||
fix, because a regression test nobody has watched fail is a regression test of unknown shape.
|
||||
|
||||
## Observations that are not open P1 items
|
||||
|
||||
Both were checked against the reviews rather than assumed, because "looks unfinished" and "is an open
|
||||
finding" are different claims.
|
||||
|
||||
**The GraphQL cursor key gate.** `GraphQlPlatformStartupValidator` refuses to start a production
|
||||
deployment without `backend.graphql.cursor.key-ids`, and nothing signs a cursor with it:
|
||||
`GraphQlConnectionAssembler` and `HmacGraphQlCursorCodec` have no consumer anywhere in this
|
||||
repository, because the template ships no paginating resolver. This is not GQL-010, which is about
|
||||
the codec's framing, rotation and scope and is implemented — versioned framing, the codec choosing
|
||||
the active key rather than the caller, v1 decode kept only for migration, tenant scope bound. It
|
||||
belongs to the `modelled` grading the leaf's own `GraphQlPolicyRequestPathTest` already documents in
|
||||
as many words. Declaring beans for it would create the unreachable-control defect this pass exists
|
||||
to close, and the present behaviour fails closed, which is the safe direction. Left as it is, on
|
||||
purpose.
|
||||
|
||||
## Product work, not remediation
|
||||
|
||||
**Notification provider transports.** Only SMTP has a `ProviderRuntimeAssembler`.
|
||||
`NotificationProviderAssembly` refuses to start a profile whose family has no assembler, naming the
|
||||
transport as a seam rather than an implementation — which is the honest fail-closed behaviour, not a
|
||||
defect. Building SES, Twilio, FCM, APNs and WebPush transports is product work.
|
||||
Reference in New Issue
Block a user