# FlowDrop Interrupt

> Human-in-the-loop interrupt system that pauses workflows to request user input via confirmations, choices, text, or JSON schema forms.

## Overview

The `flowdrop_interrupt` module enables workflows to pause execution and request user input. When a workflow reaches an interrupt node, it throws an `InterruptRequiredException` carrying a typed message DTO; a catch-side subscriber persists the interrupt entity, pauses the pipeline, and dispatches the message on the bus. Once resolved, the pipeline resumes from where it left off.

Four interrupt types are provided out of the box: confirmation (yes/no), choice selection, free-text input, and JSON schema-driven forms. The module integrates with both pipeline-based execution and session-based interactive workflows.

Since 1.4.0 the creation path is **message-driven**: node processors don't call the `InterruptManager` synchronously anymore — they throw `InterruptRequiredException(new HitlInterruptMessage(...))` (or the linked / inward-signal variants), and `JobInterruptCaughtSubscriber` runs the persistence + bus dispatch on the catch side. Two new interrupt **directions** are first-class alongside the original outward HITL prompt:

| Direction | Message DTO | Use case |
|-----------|-------------|----------|
| Outward (HITL) | `HitlInterruptMessage` | Prompt the user for input (confirmation, choice, text, form). |
| Linked | `LinkedInterruptMessage` | Pause one pipeline until another resolves — e.g. `WorkflowNode` waiting for a sub-pipeline to complete asynchronously. |
| Inward signal | `InwardSignalMessage` | Carry an operator-initiated Cancel/Pause signal to a running pipeline; `PipelineSignalPollEvent` is the bridge the orchestrator polls between jobs. |

## Dependencies

- [flowdrop](flowdrop.md)
- [flowdrop_orchestration](flowdrop_orchestration.md) (orchestrator integration)
- [flowdrop_pipeline](flowdrop_pipeline.md) (pipeline pause/resume)
- [flowdrop_job](flowdrop_job.md) (job state management)
- [flowdrop_session](flowdrop_session.md) (session-aware interrupts)
- [flowdrop_node_category](flowdrop_node_category.md) (node categorization)
- `drupal:user`

## Configuration

### Permissions

| Permission | Description |
|------------|-------------|
| `administer flowdrop interrupts` | Full administrative access. Restricted. |
| `view flowdrop interrupts` | View interrupt requests |
| `resolve flowdrop interrupts` | Respond to interrupt requests |
| `cancel flowdrop interrupts` | Cancel pending interrupt requests |

## Tips and Tricks

### Interrupt Types

| Type | Use Case | Node Processor |
|------|----------|----------------|
| **Confirmation** | Yes/No decisions (e.g., "Proceed with deletion?") | `ConfirmationNode` |
| **Choice** | Select from predefined options (single or multiple) | `ChoiceNode` |
| **Text Input** | Free-form text collection (e.g., "Enter a reason") | `TextInputNode` |
| **Form Input** | Structured data via JSON Schema forms | `FormInputNode` |

### Interrupt Resolution Flow

```
Workflow reaches interrupt node
    → Node throws InterruptRequiredException(InterruptMessageInterface)
    → JobInterruptCaughtSubscriber catches it:
        ├── InterruptFactoryInterface::create(InterruptCreationData) persists entity
        └── InterruptMessageBus dispatches message to the registered handler
    → Pipeline pauses, job marked as interrupted (status: pending)
    → User resolves via API (POST /api/flowdrop/interrupts/{id})
    → InterruptResolvedSubscriber resets job to pending
    → Pipeline resumes execution
```

For backward compatibility the synchronous `InterruptManagerInterface::createInterrupt()` and the public `InterruptRequiredException::$interrupt` property are kept as deprecated shims for 1.3.x callers; both will be removed in 2.0.0. See [#3591539](https://www.drupal.org/project/flowdrop/issues/3591539).

### Message Handlers

Handlers are attribute-discovered services tagged `flowdrop_interrupt.message_handler` and implement `InterruptMessageHandlerInterface::supports()` to claim one concrete message class. The `InterruptMessageHandlerPass` compiler pass fails the build if two handlers claim the same class or if a tagged service doesn't implement the interface — wiring errors surface at container compile, not at first dispatch.

| Handler | Persists |
|---------|----------|
| `HitlInterruptMessageHandler` | `HitlInterruptMessage` (always stamps `InterruptDirection::Outward`). |
| `LinkedInterruptMessageHandler` | `LinkedInterruptMessage` (linkage stored on the typed `linked_interrupt_id` column). |
| `InwardSignalMessageHandler` | `InwardSignalMessage` (stamps initiator + `targetPipelineId`). |
| `PipelineCompletionSentinelMessageHandler` (in `flowdrop_workflow_executor`) | `PipelineCompletionSentinelMessage` (sub-pipeline → parent linkage). |

### Session-Aware Interrupts

When interrupts occur within a [session](flowdrop_session.md) context, the `SessionInterruptResolvedSubscriber` handles additional session state updates and message creation, keeping the interactive UI in sync.

## Developer API

### Services

| Service ID | Class | Description |
|-----------|-------|-------------|
| `flowdrop_interrupt.manager` | `InterruptManager` | Resolves, cancels, and queries interrupt requests. The 11-parameter `createInterrupt()` is deprecated in 1.4.0; throw `InterruptRequiredException` or use `InterruptFactoryInterface::create()` instead. |
| `flowdrop_interrupt.factory` | `InterruptFactory` (1.4.0+) | Persists a new interrupt from an `InterruptCreationData` DTO. The `JobInterruptCaughtSubscriber` calls this on the catch side. |
| `flowdrop_interrupt.message_bus` | `InterruptMessageBus` (1.4.0+) | Dispatches an `InterruptMessageInterface` to its registered handler. |
| `flowdrop_interrupt.interrupt_resolved_subscriber` | `InterruptResolvedSubscriber` | Resets interrupted jobs and resumes pipeline execution |
| `flowdrop_interrupt.session_interrupt_resolved_subscriber` | `SessionInterruptResolvedSubscriber` | Session-aware interrupt resolution (higher priority) |

### Entities

#### FlowDropInterrupt (Content Entity)

Stores interrupt requests with the interrupt type, prompt/configuration, response data, and status (pending, resolved, cancelled).

### API Endpoints

| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/flowdrop/interrupts/{interrupt_id}` | Get interrupt details |
| POST | `/api/flowdrop/interrupts/{interrupt_id}` | Resolve an interrupt (submit response) |
| POST | `/api/flowdrop/interrupts/{interrupt_id}/cancel` | Cancel a pending interrupt |
| GET | `/api/flowdrop/playground/sessions/{session_id}/interrupts` | List interrupts for a session |
| GET | `/api/flowdrop/pipelines/{pipeline_id}/interrupts` | List interrupts for a pipeline |

### Node Processors

| Plugin | Description |
|--------|-------------|
| `ConfirmationNode` | Creates a yes/no confirmation interrupt |
| `ChoiceNode` | Creates a choice selection interrupt |
| `TextInputNode` | Creates a free-text input interrupt |
| `FormInputNode` | Creates a JSON schema form interrupt |

## References

- [flowdrop_session](flowdrop_session.md) — session-aware interrupt handling
- [flowdrop_stategraph](flowdrop_stategraph.md) — approval gates use the interrupt system
- [flowdrop_pipeline](flowdrop_pipeline.md) — pipeline pause/resume mechanics
- [flowdrop_playground](flowdrop_playground.md) — interactive UI for resolving interrupts
