Skip to content

design: per-case runtime SPI selection — allow strategy to vary per case instance at runtime #11

@mdproctor

Description

@mdproctor

Context

Several casehub components expose strategy SPIs selected at startup via config properties (e.g. Claudony's `CaseChannelLayout`, `MeshParticipationStrategy`, the planned `CaseWorkerUpdateStrategy` for SSE). These are startup-time selections — change `application.properties` and restart. Different fleet nodes can run different strategies but a single running instance cannot vary strategy per case.

The Gap

Some strategies may need to vary per case instance at runtime:

  • High-priority cases → `registry-hooks` (instant accuracy)
  • Background cases → `events-only` (minimal overhead)
  • Cases with specific channel layouts → `hybrid` with shorter heartbeat

Design Notes

The key insight: if SPIs are designed with `caseId` as a parameter on every method, a future `PerCaseDynamicStrategy` implementation can look up per-case config internally and delegate to the right sub-strategy. Call sites don't change.

// Already correct if designed this way:
interface CaseWorkerUpdateStrategy {
    void onLifecycleEvent(String caseId);
    Multi<String> subscribe(String caseId, Supplier<String> snapshotFn);
}

// Future implementation — no call site changes needed:
class PerCaseDynamicStrategy implements CaseWorkerUpdateStrategy {
    void onLifecycleEvent(String caseId) {
        resolveFor(caseId).onLifecycleEvent(caseId);
    }
    // ...
    private CaseWorkerUpdateStrategy resolveFor(String caseId) {
        // look up per-case config, priority, etc.
    }
}

Prerequisites

  • Each SPI that should support per-case selection must pass `caseId` through its interface (not just at construction time)
  • A `CaseStrategyRegistry` or similar mechanism is needed to store per-case strategy overrides
  • Design must work across fleet nodes (per-case config must be shared or replicated)

Applies to

All casehub repos that expose strategy SPIs:

  • Claudony: `CaseChannelLayout`, `MeshParticipationStrategy`, `CaseWorkerUpdateStrategy` (planned)
  • Engine: `WorkerProvisioner`, `WorkerContextProvider`, `CaseChannelProvider`
  • Qhorus: `MessageTypePolicy`, `InstanceActorIdProvider`

Not doing now

Current SPIs are startup-time via factory pattern — clean to retrofit once this design is settled. No existing call sites need to change if SPIs are designed with `caseId` in their method signatures from the start.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions