Chapter 25: Assemble the Log Incident Watcher

The capstone runs every part together. Each run is a Temporal workflow. Each model call and each tool call is an activity. Approval is a signal. Memory is Spring AI Session. Retrieval is hybrid search with a cross-encoder reranker.

Figure 25.1. Every part of the course in one picture. The workflow only schedules; the three activities do all the I/O. The figure leaves out the step log from Chapter 19 and the triage cache from Chapter 22.

Temporal’s Spring AI integration does not yet support Spring AI 2.x. The integration is in public preview with Spring AI 1.1.0 listed. Upgrading it is tracked in temporalio/ai-integrations PR #40. This chapter uses plain activities that do the same job.

ActivityRole
ModelActivityReads the session, calls the model, saves the reply last
ToolActivityChecks the allowlist, runs the tool, saves the result
AlertActivityPosts the alert, only after approval

The workflow runs the loop. It stops on a final answer, a submitFinding tool call, the step limit, loop detection, an approval timeout, or a rejection. It never calls a model or a tool itself.

Stop ruleLimitStopReason
Final answer—FINAL_ANSWER
submitFinding tool call—NEEDS INPUT: which StopReason a submitFinding stop returns; the enum has no dedicated value
Step limitMAX_STEPS = 12STEP_LIMIT
Loop detectionMAX_REPEATS = 3LOOP_DETECTED
Approval timeoutone hour (Chapter 9)APPROVAL_TIMEOUT
Rejection—REJECTED

Table 25.1. The capstone's stop rules mapped to the StopReason enum from Chapter 6.

@WorkflowImpl(workers = "watcher")
public class WatcherWorkflowImpl implements WatcherWorkflow {
    private static final int MAX_STEPS = 12;
    private static final int MAX_REPEATS = 3;
    // activity stubs for model, tools, and alerts
    // loop: model turn, then each tool call, then stop rules
}
Figure 25.2. A critical run, end to end. Step through it with Previous and Next. Placing the approval after submitFinding combines this chapter with Chapter 9.

Rule to teach. A retried activity can save its message twice if it fails after saving. Save last. Verify whether Session accepts a message id for idempotent saves.

Switching later. When io.temporal:spring-ai ships a public release, replace the model activity with its chat model class. The stop rules and approval code stay the same.

Final exercise. Run the full eval set. Break one drill from Chapter 21, fix it, and record the final score.

docs