RAS was wearing a reactive costume. Every one of its 226 Uni references fell into exactly two categories: Uni.createFrom().item(synchronousResult) on the provider side, .await().indefinitely() on the consumer side. Zero actual reactive composition anywhere — no flatMap chains, no backpressure, no async pipelines.

I found this by tracing from the SPIs outward. Ganglion.detect() returns Uni<DetectionResult>, but every implementation — ExpressionRulesGanglion, NaiveBayesGanglion, DroolsGanglion, JavaSwitchGanglion — computes the result synchronously and wraps it. The callers — SituationEvaluator, SituationExpiryJob — immediately unwrap with .await().indefinitely(). Same pattern across SituationStore (9 methods), GanglionStateStore (4 methods), RasTriggerPolicy, CaseTrigger, SituationSource, OrphanedResourceCleaner. Twenty-one methods across seven interfaces, all ceremony.

The question was whether RAS should have had dual-stack parity — blocking and reactive variants side by side, like qhorus and platform. It shouldn’t have. RAS uses blocking JPA with EntityManager and @Transactional. It processes CDI events synchronously. There was never a reactive I/O path to justify the Uni signatures. The right design from day one was blocking returns.

The fix was straightforward: Uni<T>T, Uni<Void>void, across all SPIs and every implementation. The one interesting edge is DefaultCaseTrigger.fire(), which wraps CaseHub.startCase() — that returns CompletionStage<UUID> from engine-api. For now it’s .toCompletableFuture().join(). When parent#381 retires the engine’s CompletionStage returns, the .join() disappears and the method becomes a direct call.

We changed 52 files in RAS, then fixed three consumer repos in the same pass — ops, desiredstate, and iot. The consumer impact was minimal: JavaSwitchGanglion subclasses override evaluate() which was always blocking, so they compiled without changes. Only the repos implementing RasTriggerPolicy (IoTSuppressionTriggerPolicy in iot) or calling SPI methods directly needed updates. Mutiny is gone from every POM.

The broader point: RAS is now the first foundation-tier repo fully blocking under ADR-0005. The remaining repos in parent#384 — platform, qhorus, neocortex, and the rest — have genuine dual-stack to delete, not costume to remove.


<
Previous Post
Thirteen Repos, One Branch Name
>
Next Post
Twelve Out of Thirteen