10 KiB
Task 6 — the five reviews' P0 findings, reconciled
The remediation design this wave executed never referenced a review id (grep -c over it: zero).
It took one theme from the five reviews — five adapters ship and do not run — and Waves 0–6 executed
that theme. So "how much of the reviews is reflected" had never been measured. This is the first
measurement, and then the work that followed it.
Three read-only audits ran in parallel, one per review family, each instructed to treat a passing test as evidence only when it exercises the real composition. Their headline claims were then re-checked by hand before being acted on; one was wrong and is recorded as such below.
Where the P0 set stood when measured
| Review | P0 | CLOSED | PARTIAL | OPEN |
|---|---|---|---|---|
| jpa | 6 | 5 | 1 | 0 |
| messaging | 5 | 2 | 3 | 0 |
| graphql | 2 | 1 | 1 | 0 |
| notification | 12 | 1 | 11 | 0 |
| mongodb | 9 | 0 | 9 | 0 |
| total | 34 | 9 | 25 | 0 |
Nothing was OPEN: every P0 had been worked. What the audits found instead, in three independent voices, was the same shape — the implementation is substantially real and the gate under it is thin. Six notification adapter classes were referenced by zero tests. Both Mongo executors were referenced by zero tests. Every messaging real-broker test skips silently without Docker. And the Compose matrix, the strongest evidence this repository produces, runs in no workflow.
What was fixed, and how each was proven
NTF-004 / JPA-004 — the completion write was not fenced
Two audits reached this independently from different reviews, which is why it was taken first.
The claim is fenced (a single FOR UPDATE SKIP LOCKED CTE that bumps lease_fence) and the renew
is fenced. The completion was findById → mutate → saveAndFlush with no owner or fence predicate.
A worker whose lease expired during the provider call — the one stretch the platform deliberately
spends outside a transaction — came back and wrote its outcome over the row a new holder had already
claimed. The @Version column does not stop that: it detects a concurrent edit, not a superseded
writer, and the late worker's read is recent enough to win.
Fixed with conditional statements in the same idiom as the renew, saveHeldBy/transitionHeldBy on
the port returning empty when the lease is gone, and all four branches of applyNextAction moved
onto them. Losing the lease is not an error: the new holder owns the job and will record its own
outcome.
@Modifying(clearAutomatically = true) on both, because a native update bypasses the persistence
context and the immediate re-read would otherwise be served the values it just replaced — a trap one
of the audits had found elsewhere in this same platform.
Proof: two real-PostgreSQL cases in the JPA contract lane. A superseded holder's completion matches zero rows and the live holder's state is untouched; the current holder's completion writes. The second exists because without it the first is satisfied by a statement that matches nothing ever.
NTF-012 — the SSRF guard had no callers
requireExternallyRoutable refuses the cloud metadata service, RFC 1918, link-local, IPv6 local,
userinfo disguise and multi-answer DNS. It had an eight-case test suite, all green, and one
occurrence in the repository: its own definition. The two sites it was written for —
WebhookSubscription and SesProviderProperties — still called requireSecureOrLoopback, which
reads the scheme and nothing else.
Both now call it. The new test goes through the constructors rather than the helper, because testing the helper again is exactly what failed to catch this.
What is not closed: allowLoopback is true, so a user-supplied target naming localhost still
passes. Closing it means the allowance becomes a decision the caller states, and the caller does not
exist — WEBHOOK has no ProviderRuntimeAssembler, so a webhook profile refuses to boot and nothing
in production constructs the record. Writing the policy now would mean choosing its default with no
caller to check it against. Recorded in the code at the call site.
NTF-001 — the documented callback switch broke startup
CallbackRequestFactory is a constructor argument of the MVC controller, the WebFlux handler and the
WebFlux configuration, and was produced by no production code — the only instantiation was in a test.
So APP_NOTIFICATION_PLATFORM_CALLBACKS_ENABLED=true, a key in the env registry and in the
configuration reference, did not enable callbacks; it failed the boot on an unsatisfied dependency.
The beans now exist, conditioned on the same switch. The missing trusted-proxies setting came with
them, defaulting to empty — with no entry, forwarded headers are not believed, because honouring
them unconditionally lets any caller choose the URL its own signature is checked against. Registered
in application.yml, the env registry, .env.example and the configuration reference; both
notification gates pass.
MNG-007 — a health view Actuator could not read, and a reactive half nothing built
MongoPlatformHealthIndicator computed topology mismatch, secondary availability and a bounded
detail map, and implemented neither HealthIndicator nor HealthContributor. The adapter carries no
Actuator dependency and should not: the delivery platform had already established the shape, where
the adapter computes the facts and the composition root maps them onto Health. Done the same way.
Separately, every reactive class in the leaf — executor, consistency binder, session factory, cursor
guard — was declared by no configuration. They shipped and no configuration could construct them.
Now wired in a nested configuration conditioned on a ReactiveMongoTemplate bean, not just the
class: the class is on the compile classpath unconditionally, so a class condition alone would try to
build the reactive path in a servlet-only deployment and fail a startup for a capability nobody asked
for. Both the positive and the negative case are asserted.
MNG-005 / MNG-023 — a deadline that only produced a report
The blocking executor compared elapsed time to the declared timeout after the callback returned, and said so in its own comment: a Java callback cannot be interrupted mid driver call. That is an overrun report, not a deadline.
The scoped API is the narrowed surface where the number can actually be sent, so every method taking
a Query or an Aggregation now carries it as maxTimeMS, which the server enforces. insert has
no query to attach it to. Duration.ZERO is refused, because zero means "no limit" to the server and
accepting it would turn a misconfiguration into an unbounded operation.
The raw escape hatches (rawOperations(), executeInternal(...)) are genuinely used inside the
platform by the geospatial, atomic and bulk operations, and their callers live in sibling packages,
so package-private cannot express the rule. It is enforced as a boundary from the composition root —
the only place that sees both the platform and everything consuming it. The rule was falsified by
widening its scope until it fired, and a second case asserts the platform still uses them, so the
rule cannot pass by the hatches having been deleted.
MNG-006 — a guard that compared a declaration against nothing
LocalDateTimeMappingGuard was constructed withoutConverters() and then asked to validate the
manifest. It could only ever reject LOCAL_DATE_TIME_WITH_REGISTERED_CONVERTER: a deployment that
had registered the named converter was rejected exactly as loudly as one that had not, so the check
that exists to distinguish those two cases could not tell them apart. It now reads the converters the
deployment actually registered, and a test asserts the same manifest passes with the converter and
fails without it.
MNG-008 — a promotion gate that required five of the six categories it declares
MongoAdvancedPromotionEvidence.REQUIRED listed six; MongoAdvancedPromotionGate.verify() required
five. migration was missing, so a promotion could pass with no migration evidence at all.
GQL-002 — the batch policy applied to nothing
GraphQlBatchLoaderRegistrar carried the chunking, the budget and the request scope, was unit
tested, and was declared by no configuration — the only file mentioning it was itself. A field
resolving through @BatchMapping or a DataLoader met none of it. The chain
(BatchPolicyRegistry → DataLoaderFactory → BatchLoaderRegistrar) is now assembled by the platform.
The batch ceiling reuses maximumPageSize rather than adding a setting: both answer how many rows
one downstream call may ask for, and a batch limit above the page limit would let one request fan out
past the bound it already accepted.
One thing an audit got wrong, and one fix that was reverted
The mongodb audit reported that the UUID axis is "declaration-only" and mapped onto the driver
nowhere. It is mapped — MongoClientSettingsFactory calls .uuidRepresentation(...). Verified
before acting.
Acting on the adjacent concern — the manifest and the profile describing the same fact independently
— a startup check was written to refuse a disagreement between them. Writing its test showed the
disagreement cannot occur: MongoUuidRepresentation has two values, only STANDARD is writable, and
an existing check already refuses the other. A guard for a state that cannot arise is the same
"declaration nothing checks" this wave has been removing, so it was reverted rather than kept with
an unfalsifiable test.
What remains, and why
- NTF-012 loopback residue — belongs with the change set that gives WEBHOOK an assembler.
- MSG-015 — the application-owned port and anti-corruption bridge between
application-coreand the messaging platform. The review names it and specifies the target shape; it is a runtime wiring change, and the review itself says the physical work is a separate change set. - MSG-003/004/005 residue — on code no deployment can execute: the platform's outbox and inbox migrations are applied only by tests, the production Kafka transport is publish-only, and Rabbit declares no transport bean.
- P1 and P2 — 100 findings, never audited. They were not in this remediation's scope and their state is unknown. Saying so is the honest position; the P0 audit took three parallel agents and the P1/P2 set is three times larger.