Skip to content

Commit c07fdea

Browse files
committed
Replace EscalationKeyword with Escalation sealed class hierarchy
Introduce a proper domain model for escalation types: - EscalationProcess: Defines resolution mechanisms (AgentMeeting, HumanApproval, HumanMeeting, ExternalDependency) - Escalation: Sealed class hierarchy with Discussion, Decision, Budget, Priorities, Scope, and External categories Each escalation type defines its process, making the model suitable for LLM classification instead of keyword matching. The TicketOrchestrator now accepts an optional Escalation type to determine meeting and participant requirements.
1 parent db6a36c commit c07fdea

6 files changed

Lines changed: 768 additions & 425 deletions

File tree

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
package link.socket.kore.agents.events.tickets
2+
3+
/**
4+
* Represents the type of escalation needed for a blocker.
5+
* Each escalation type defines what kind of issue it is and how it should be resolved.
6+
*
7+
* This hierarchy is designed to be used with LLM classification - an LLM analyzes
8+
* a blocker scenario and categorizes it into the appropriate escalation type.
9+
*/
10+
sealed class Escalation {
11+
12+
/**
13+
* The process mechanism used to resolve this type of escalation.
14+
*/
15+
abstract val process: EscalationProcess
16+
17+
/**
18+
* Human-readable description of this escalation type for LLM context.
19+
*/
20+
abstract val description: String
21+
22+
// ==================== Discussion Escalations ====================
23+
24+
/**
25+
* Escalations that require collaborative discussion to resolve.
26+
* These typically involve technical topics where multiple perspectives
27+
* help reach a better solution.
28+
*/
29+
sealed class Discussion : Escalation() {
30+
31+
/**
32+
* Code needs review before proceeding.
33+
* Includes pull requests, implementation approaches, and code quality concerns.
34+
*/
35+
data object CodeReview : Discussion() {
36+
override val process = EscalationProcess.AgentMeeting
37+
override val description = "Code needs review - includes PRs, implementation approaches, and quality concerns"
38+
}
39+
40+
/**
41+
* Design decisions need discussion.
42+
* Includes UI/UX design, API design, and system design choices.
43+
*/
44+
data object Design : Discussion() {
45+
override val process = EscalationProcess.AgentMeeting
46+
override val description = "Design decisions needed - UI/UX, API design, or system design choices"
47+
}
48+
49+
/**
50+
* Architecture questions need clarification.
51+
* Includes system structure, component relationships, and technical patterns.
52+
*/
53+
data object Architecture : Discussion() {
54+
override val process = EscalationProcess.AgentMeeting
55+
override val description = "Architecture clarification needed - system structure, components, or patterns"
56+
}
57+
58+
/**
59+
* Requirements need clarification or refinement.
60+
* Includes unclear specifications, missing details, or conflicting requirements.
61+
*/
62+
data object Requirements : Discussion() {
63+
override val process = EscalationProcess.HumanMeeting
64+
override val description = "Requirements clarification needed - unclear specs, missing details, or conflicts"
65+
}
66+
}
67+
68+
// ==================== Decision Escalations ====================
69+
70+
/**
71+
* Escalations that require a decision to be made.
72+
* These block progress until someone with authority makes a choice.
73+
*/
74+
sealed class Decision : Escalation() {
75+
76+
/**
77+
* Technical decision needed between alternatives.
78+
* Includes technology choices, library selection, and implementation strategies.
79+
*/
80+
data object Technical : Decision() {
81+
override val process = EscalationProcess.AgentMeeting
82+
override val description = "Technical decision needed - technology choices, libraries, or implementation strategies"
83+
}
84+
85+
/**
86+
* Product decision needed from stakeholders.
87+
* Includes feature direction, user experience choices, and business logic decisions.
88+
*/
89+
data object Product : Decision() {
90+
override val process = EscalationProcess.HumanMeeting
91+
override val description = "Product decision needed - feature direction, UX choices, or business logic"
92+
}
93+
94+
/**
95+
* Authorization or approval needed to proceed.
96+
* Includes security approvals, compliance sign-offs, and access grants.
97+
*/
98+
data object Authorization : Decision() {
99+
override val process = EscalationProcess.HumanApproval
100+
override val description = "Authorization needed - security approvals, compliance, or access grants"
101+
}
102+
}
103+
104+
// ==================== Budget Escalations ====================
105+
106+
/**
107+
* Escalations related to budget, resources, or costs.
108+
* These typically require human approval due to financial implications.
109+
*/
110+
sealed class Budget : Escalation() {
111+
112+
/**
113+
* Resource allocation decision needed.
114+
* Includes team capacity, infrastructure resources, and tool licenses.
115+
*/
116+
data object ResourceAllocation : Budget() {
117+
override val process = EscalationProcess.HumanMeeting
118+
override val description = "Resource allocation needed - team capacity, infrastructure, or licenses"
119+
}
120+
121+
/**
122+
* Cost approval needed for expenditure.
123+
* Includes service costs, tool purchases, and infrastructure expenses.
124+
*/
125+
data object CostApproval : Budget() {
126+
override val process = EscalationProcess.HumanApproval
127+
override val description = "Cost approval needed - service costs, purchases, or infrastructure expenses"
128+
}
129+
130+
/**
131+
* Timeline or deadline negotiation needed.
132+
* Includes scope-timeline tradeoffs and delivery date discussions.
133+
*/
134+
data object Timeline : Budget() {
135+
override val process = EscalationProcess.HumanMeeting
136+
override val description = "Timeline negotiation needed - scope-timeline tradeoffs or delivery dates"
137+
}
138+
}
139+
140+
// ==================== Priorities Escalations ====================
141+
142+
/**
143+
* Escalations related to prioritization and scheduling.
144+
* These involve deciding what to work on and in what order.
145+
*/
146+
sealed class Priorities : Escalation() {
147+
148+
/**
149+
* Conflicting priorities need resolution.
150+
* Includes competing deadlines, resource conflicts, and dependency ordering.
151+
*/
152+
data object Conflict : Priorities() {
153+
override val process = EscalationProcess.HumanMeeting
154+
override val description = "Priority conflict - competing deadlines, resource conflicts, or dependency ordering"
155+
}
156+
157+
/**
158+
* Reprioritization request for existing work.
159+
* Includes urgent requests, changed business needs, and blocking issues.
160+
*/
161+
data object Reprioritization : Priorities() {
162+
override val process = EscalationProcess.HumanApproval
163+
override val description = "Reprioritization request - urgent requests, changed needs, or blocking issues"
164+
}
165+
166+
/**
167+
* Dependency on another team or project.
168+
* Includes cross-team coordination and external project dependencies.
169+
*/
170+
data object Dependency : Priorities() {
171+
override val process = EscalationProcess.AgentMeeting
172+
override val description = "Cross-team dependency - coordination with other teams or projects"
173+
}
174+
}
175+
176+
// ==================== Scope Escalations ====================
177+
178+
/**
179+
* Escalations related to project scope and boundaries.
180+
* These affect what is and isn't included in the work.
181+
*/
182+
sealed class Scope : Escalation() {
183+
184+
/**
185+
* Scope expansion or feature creep detected.
186+
* Includes additional requirements, expanded functionality, and new use cases.
187+
*/
188+
data object Expansion : Scope() {
189+
override val process = EscalationProcess.HumanMeeting
190+
override val description = "Scope expansion detected - additional requirements or new use cases"
191+
}
192+
193+
/**
194+
* Scope reduction or feature cut needed.
195+
* Includes timeline pressure, technical limitations, and resource constraints.
196+
*/
197+
data object Reduction : Scope() {
198+
override val process = EscalationProcess.HumanMeeting
199+
override val description = "Scope reduction needed - timeline pressure or resource constraints"
200+
}
201+
202+
/**
203+
* Scope boundaries unclear or undefined.
204+
* Includes ambiguous requirements and undefined edge cases.
205+
*/
206+
data object Clarification : Scope() {
207+
override val process = EscalationProcess.HumanMeeting
208+
override val description = "Scope clarification needed - ambiguous requirements or undefined boundaries"
209+
}
210+
}
211+
212+
// ==================== External Escalations ====================
213+
214+
/**
215+
* Escalations caused by external factors outside the team's control.
216+
*/
217+
sealed class External : Escalation() {
218+
219+
/**
220+
* Waiting for third-party vendor or service.
221+
* Includes API availability, support responses, and service provisioning.
222+
*/
223+
data object Vendor : External() {
224+
override val process = EscalationProcess.ExternalDependency
225+
override val description = "Waiting for vendor - API availability, support, or service provisioning"
226+
}
227+
228+
/**
229+
* Waiting for customer or end-user input.
230+
* Includes user testing feedback, customer requirements, and stakeholder input.
231+
*/
232+
data object Customer : External() {
233+
override val process = EscalationProcess.HumanApproval
234+
override val description = "Waiting for customer - user feedback, requirements, or stakeholder input"
235+
}
236+
}
237+
238+
companion object {
239+
/**
240+
* Returns all possible escalation types for LLM classification context.
241+
* The LLM uses these descriptions to categorize blocker scenarios.
242+
*/
243+
fun allTypes(): List<Escalation> = listOf(
244+
// Discussion
245+
Discussion.CodeReview,
246+
Discussion.Design,
247+
Discussion.Architecture,
248+
Discussion.Requirements,
249+
// Decision
250+
Decision.Technical,
251+
Decision.Product,
252+
Decision.Authorization,
253+
// Budget
254+
Budget.ResourceAllocation,
255+
Budget.CostApproval,
256+
Budget.Timeline,
257+
// Priorities
258+
Priorities.Conflict,
259+
Priorities.Reprioritization,
260+
Priorities.Dependency,
261+
// Scope
262+
Scope.Expansion,
263+
Scope.Reduction,
264+
Scope.Clarification,
265+
// External
266+
External.Vendor,
267+
External.Customer,
268+
)
269+
270+
/**
271+
* Returns a formatted string of all escalation types and their descriptions
272+
* suitable for use in LLM prompts.
273+
*/
274+
fun allTypesForPrompt(): String = buildString {
275+
appendLine("Available escalation types:")
276+
appendLine()
277+
278+
appendLine("## Discussion")
279+
appendLine("- CodeReview: ${Discussion.CodeReview.description}")
280+
appendLine("- Design: ${Discussion.Design.description}")
281+
appendLine("- Architecture: ${Discussion.Architecture.description}")
282+
appendLine("- Requirements: ${Discussion.Requirements.description}")
283+
appendLine()
284+
285+
appendLine("## Decision")
286+
appendLine("- Technical: ${Decision.Technical.description}")
287+
appendLine("- Product: ${Decision.Product.description}")
288+
appendLine("- Authorization: ${Decision.Authorization.description}")
289+
appendLine()
290+
291+
appendLine("## Budget")
292+
appendLine("- ResourceAllocation: ${Budget.ResourceAllocation.description}")
293+
appendLine("- CostApproval: ${Budget.CostApproval.description}")
294+
appendLine("- Timeline: ${Budget.Timeline.description}")
295+
appendLine()
296+
297+
appendLine("## Priorities")
298+
appendLine("- Conflict: ${Priorities.Conflict.description}")
299+
appendLine("- Reprioritization: ${Priorities.Reprioritization.description}")
300+
appendLine("- Dependency: ${Priorities.Dependency.description}")
301+
appendLine()
302+
303+
appendLine("## Scope")
304+
appendLine("- Expansion: ${Scope.Expansion.description}")
305+
appendLine("- Reduction: ${Scope.Reduction.description}")
306+
appendLine("- Clarification: ${Scope.Clarification.description}")
307+
appendLine()
308+
309+
appendLine("## External")
310+
appendLine("- Vendor: ${External.Vendor.description}")
311+
appendLine("- Customer: ${External.Customer.description}")
312+
}
313+
314+
/**
315+
* Parses an escalation type from a string identifier.
316+
* Used to convert LLM output back to typed escalation.
317+
*
318+
* @param identifier The escalation type identifier (e.g., "Discussion.CodeReview")
319+
* @return The matching Escalation type, or null if not found.
320+
*/
321+
fun fromIdentifier(identifier: String): Escalation? {
322+
val normalized = identifier.trim()
323+
return when {
324+
// Discussion
325+
normalized.contains("CodeReview", ignoreCase = true) -> Discussion.CodeReview
326+
normalized.contains("Design", ignoreCase = true) &&
327+
!normalized.contains("Redesign", ignoreCase = true) -> Discussion.Design
328+
normalized.contains("Architecture", ignoreCase = true) -> Discussion.Architecture
329+
normalized.contains("Requirements", ignoreCase = true) -> Discussion.Requirements
330+
331+
// Decision
332+
normalized.contains("Technical", ignoreCase = true) -> Decision.Technical
333+
normalized.contains("Product", ignoreCase = true) -> Decision.Product
334+
normalized.contains("Authorization", ignoreCase = true) -> Decision.Authorization
335+
336+
// Budget
337+
normalized.contains("ResourceAllocation", ignoreCase = true) -> Budget.ResourceAllocation
338+
normalized.contains("CostApproval", ignoreCase = true) -> Budget.CostApproval
339+
normalized.contains("Timeline", ignoreCase = true) -> Budget.Timeline
340+
341+
// Priorities
342+
normalized.contains("Conflict", ignoreCase = true) -> Priorities.Conflict
343+
normalized.contains("Reprioritization", ignoreCase = true) -> Priorities.Reprioritization
344+
normalized.contains("Dependency", ignoreCase = true) -> Priorities.Dependency
345+
346+
// Scope
347+
normalized.contains("Expansion", ignoreCase = true) -> Scope.Expansion
348+
normalized.contains("Reduction", ignoreCase = true) -> Scope.Reduction
349+
normalized.contains("Clarification", ignoreCase = true) -> Scope.Clarification
350+
351+
// External
352+
normalized.contains("Vendor", ignoreCase = true) -> External.Vendor
353+
normalized.contains("Customer", ignoreCase = true) -> External.Customer
354+
355+
else -> null
356+
}
357+
}
358+
}
359+
}

0 commit comments

Comments
 (0)