Platform#24 opened a question: where do application-layer SPIs live when they’re domain-agnostic but too specific for the foundation tier? The proposed answer was a casehub-platform-apps-api module. The actual answer, after working through the design, was somewhere else entirely.

The initial content was SlaBreachPolicy — a decision-returning SPI for handling WorkItem SLA breaches. Once I looked at the types involved (BreachedTask with taskId, callerRef, candidateGroups), it was obvious: these are work vocabulary, not platform vocabulary. A module claiming to be domain-agnostic but containing only work-specific types is misleading from day one.

The deeper question was whether casehub-work-api could take the dependency. SlaBreachContext needs Path and Preferences from platform-api. I had assumed “zero casehubio deps” was a hard constraint on casehub-work-api. It isn’t — any casehub repo may depend on casehub-platform-api. Once that was clear, the answer was obvious: SlaBreachPolicy and the supporting types belong in casehub-work-api, alongside EscalationPolicy and ClaimSlaPolicy where they fit naturally.

We also settled that SlaBreachPolicy replaces EscalationPolicy rather than complementing it. Both answer “what should happen when a task breaches its deadline?” — but EscalationPolicy is void and action-taking, which collapses decision and execution into one untestable call. SlaBreachPolicy returns a BreachDecision value — casehub-work executes it, fires a CDI event, observers handle side effects. The policy stays pure.

Platform#24 is closed. The full type design landed in casehubio/work#213, ready for the next session in that repo.

casehub-platform-apps-api remains a valid future home for cross-cutting application SPIs that genuinely span all modules. None have surfaced yet.


<
Previous Post
The Decision the Policy Returns
>
Next Post
The last piece of the diff viewer was the hardest