Parent: #379 (ADR-0005)
Blocked by: #381 (engine SPIs must land first)
What
Delete the reactive tier across all non-engine repos. Every reactive SPI has a blocking counterpart — no new code needed. Same mechanical pattern as engine (#381): delete reactive, re-wire to blocking, virtual threads handle concurrency.
Audit Result: 100% Dual-Stack Everywhere
No reactive-only paths found. Every Reactive* class has a blocking counterpart.
Qhorus (heaviest — ~11 dual-stack pairs + services)
| Blocking |
Reactive |
Delete |
| ChannelStore |
ReactiveChannelStore |
Reactive JPA, InMemory, tests |
| MessageStore |
ReactiveMessageStore |
Reactive JPA, InMemory, tests |
| CommitmentStore |
ReactiveCommitmentStore |
Reactive JPA, InMemory, tests |
| DataStore |
ReactiveDataStore |
Reactive JPA, InMemory, tests |
| ChannelService |
ReactiveChannelService |
Reactive service + tests |
| MessageService |
ReactiveMessageService |
Reactive service + tests |
| CommitmentService |
ReactiveCommitmentService |
Reactive service |
| DataService |
ReactiveDataService |
Reactive service |
| InstanceService |
ReactiveInstanceService |
Reactive service |
| WatchdogService |
ReactiveWatchdogService |
Reactive service |
| A2AResource |
ReactiveA2AResource |
Reactive REST |
| QhorusMcpTools |
ReactiveQhorusMcpTools |
Reactive MCP |
| LedgerWriteService |
ReactiveLedgerWriteService |
Reactive ledger |
| AgentCardResource |
ReactiveAgentCardResource |
Reactive REST |
| ProjectionService |
ReactiveProjectionService |
Reactive service |
Also: remove 42 @Blocking annotations, @IfBuildProperty gating, QhorusBuildTimeConfig.reactive().
Keep: postgres-broadcaster (LISTEN/NOTIFY), @RunOnVirtualThread on SSE/WebSocket.
Platform (2 dual-stack pairs)
| Blocking |
Reactive |
Delete |
| NotificationStore |
ReactiveNotificationStore |
JPA, InMemory, NoOp reactive impls |
| SubscriptionStore |
ReactiveSubscriptionStore |
JPA, InMemory, NoOp reactive impls |
Neocortex (~10 dual-stack pairs + decorators)
| Blocking |
Reactive |
Delete |
| CaseMemoryStore |
ReactiveCaseMemoryStore |
Reactive interface + InMemory impl |
| CbrCaseMemoryStore |
ReactiveCbrCaseMemoryStore |
Reactive interface + InMemory impl |
| GraphCaseMemoryStore |
ReactiveGraphCaseMemoryStore |
Reactive interface |
| CaseRetriever |
ReactiveCaseRetriever |
Reactive interface + InMemory impl |
| EmbeddingIngestor |
ReactiveEmbeddingIngestor |
Reactive interface |
| CorpusStore |
ReactiveCorpusStore |
Reactive interface |
| CorpusReader |
ReactiveCorpusReader |
Reactive interface |
| ChangeSource |
ReactiveChangeSource |
Reactive interface |
| CbrRetrievalTracker |
ReactiveCbrRetrievalTracker |
Reactive interface |
| RetrievalTracker |
ReactiveRetrievalTracker |
Reactive interface |
| + decorators: ReactiveOutcomeWeighting*, ReactiveTemporalDecay*, ReactiveScopeDecay*, ReactiveTrendEnrichment*, ReactiveTracking*, ReactiveReranking*, ReactiveCorrective*, ReactiveHybrid*, ReactiveQueryExpanding* |
All have blocking counterparts |
|
| + bridges: BlockingToReactive*Bridge classes |
Delete entirely |
|
Ledger, Eidos, Work
Consume engine SPIs — reactive usage retires when engine SPIs change (#381).
Keep: postgres-broadcaster modules in work.
App repos (claudony, openclaw, ras, ops, iot, blocks)
Implement engine Reactive* SPIs — switch to blocking variants after #381 lands.
Repos Touched
qhorus, platform, neocortex, ledger, eidos, work, claudony, openclaw, ras, ops, iot, blocks, desiredstate
Execution
One work-slot spanning all repos. Mechanical deletion + re-wiring per the engine cookbook.
What Stays Reactive (DO NOT DELETE)
- Reactive Messaging channels (Kafka, AMQP) — SmallRye Reactive Messaging API is
Multi<T> by design. Any @Incoming/@Outgoing annotated methods stay as-is.
Multi<T> streaming with backpressure — no virtual thread equivalent for backpressure. Includes desiredstate event streaming.
- Postgres LISTEN/NOTIFY broadcasters — event-driven pub/sub in platform, work, qhorus. Keep
quarkus-reactive-pg-client in broadcaster modules.
- SSE endpoints — already
@RunOnVirtualThread (qhorus A2A), no change needed
- WebSocket endpoints — already
@RunOnVirtualThread (qhorus, drafthouse), no change needed
@RunOnVirtualThread annotations — keep all existing ones
Parent: #379 (ADR-0005)
Blocked by: #381 (engine SPIs must land first)
What
Delete the reactive tier across all non-engine repos. Every reactive SPI has a blocking counterpart — no new code needed. Same mechanical pattern as engine (#381): delete reactive, re-wire to blocking, virtual threads handle concurrency.
Audit Result: 100% Dual-Stack Everywhere
No reactive-only paths found. Every Reactive* class has a blocking counterpart.
Qhorus (heaviest — ~11 dual-stack pairs + services)
Also: remove 42
@Blockingannotations,@IfBuildPropertygating,QhorusBuildTimeConfig.reactive().Keep: postgres-broadcaster (LISTEN/NOTIFY),
@RunOnVirtualThreadon SSE/WebSocket.Platform (2 dual-stack pairs)
Neocortex (~10 dual-stack pairs + decorators)
Ledger, Eidos, Work
Consume engine SPIs — reactive usage retires when engine SPIs change (#381).
Keep: postgres-broadcaster modules in work.
App repos (claudony, openclaw, ras, ops, iot, blocks)
Implement engine Reactive* SPIs — switch to blocking variants after #381 lands.
Repos Touched
qhorus, platform, neocortex, ledger, eidos, work, claudony, openclaw, ras, ops, iot, blocks, desiredstate
Execution
One work-slot spanning all repos. Mechanical deletion + re-wiring per the engine cookbook.
What Stays Reactive (DO NOT DELETE)
Multi<T>by design. Any@Incoming/@Outgoingannotated methods stay as-is.Multi<T>streaming with backpressure — no virtual thread equivalent for backpressure. Includes desiredstate event streaming.quarkus-reactive-pg-clientin broadcaster modules.@RunOnVirtualThread(qhorus A2A), no change needed@RunOnVirtualThread(qhorus, drafthouse), no change needed@RunOnVirtualThreadannotations — keep all existing ones