# FlowDrop Upgrade Guide

This document covers breaking changes and upgrade steps for FlowDrop. Since no stable release has been made yet, this serves as a pre-release reference for tracking changes that affect existing installations.

## Table of Contents

- [1.0.0-beta1 to 1.0.0-beta2](#100-beta1-to-100-beta2)
- [1.0.0-alpha5 to 1.0.0-beta1](#100-alpha5-to-100-beta1)
- [1.0.0-alpha4 to 1.0.0-alpha5](#100-alpha4-to-100-alpha5)
- [1.0.0-alpha3 to 1.0.0-alpha4](#100-alpha3-to-100-alpha4)
- [1.0.0-alpha2 to 1.0.0-alpha3](#100-alpha2-to-100-alpha3)
- [1.0.0-alpha1 to 1.0.0-alpha2](#100-alpha1-to-100-alpha2)
- [0.6.x to 1.0.0-alpha1](#06x-to-100-alpha1)
- [Pre-Release to 1.0.0](#pre-release-to-100)
  - [Database Updates](#database-updates)
  - [API Changes](#api-changes)
  - [Configuration Changes](#configuration-changes)
- [Version 0.4.x Changes](#version-04x-changes)
- [Version 0.3.x Changes](#version-03x-changes)

---

## 1.0.0-beta1 to 1.0.0-beta2

### WorkflowExecutionServiceInterface::execute() Mode Parameter (Breaking)

The `$mode` parameter type has changed from `string` to `WorkflowExecutionMode` enum. Code passing string literals or constants will cause a fatal type error.

```php
use Drupal\flowdrop_workflow_executor\Enum\WorkflowExecutionMode;

// Before (broken in beta2)
$service->execute($workflow, $inputData, 'job');
$service->execute($workflow, $inputData, 'job_async');
$service->execute($workflow, $inputData, 'job_fire_forget');

// After
$service->execute($workflow, $inputData, WorkflowExecutionMode::Job);
$service->execute($workflow, $inputData, WorkflowExecutionMode::JobAsync);
$service->execute($workflow, $inputData, WorkflowExecutionMode::JobFireForget);
```

The enum is in `Drupal\flowdrop_workflow_executor\Enum\WorkflowExecutionMode` and carries the same string values (`'job'`, `'job_async'`, `'job_fire_forget'`) as the former constants, so serialized values and config are unaffected — only PHP call sites need updating.

### FlowDropEventTypeInterface::getCategory() Return Type (Breaking)

`FlowDropEventTypeInterface::getCategory()` now declares a typed return of `EventCategory` enum instead of `string`. Any code that type-checks the return value as `string`, passes it where a `string` is expected, or compares it with `===` against a string literal will break.

```php
use Drupal\flowdrop_trigger\Enum\EventCategory;

// Before (broken in beta2)
$category = $eventType->getCategory(); // returned e.g. 'entity'
if ($category === 'entity') { ... }

// After
$category = $eventType->getCategory(); // returns EventCategory enum case
if ($category === EventCategory::Entity) { ... }

// Available cases:
// EventCategory::Entity  ('entity')
// EventCategory::User    ('user')
// EventCategory::Form    ('form')
// EventCategory::Cron    ('cron')
```

If your custom event type plugin previously returned a string from `getCategory()`, update it to return the appropriate `EventCategory` case.

### Post-Update Steps

```bash
drush cr
```

No database updates are required for this release.

---

## 1.0.0-alpha5 to 1.0.0-beta1

No breaking changes. This release focuses on stabilization, documentation, and annotation policy fixes.

### Post-Update Steps

```bash
drush cr
```

No database updates are required for this release.

---

## 1.0.0-alpha4 to 1.0.0-alpha5

### Typed Event System (Breaking)

All FlowDrop event dispatching has migrated from Symfony's `GenericEvent` to 26 purpose-built event classes with `readonly` properties. If you have custom event subscribers listening to FlowDrop events, you must update them.

**Type hint changes:**

```php
// Before
use Symfony\Component\EventDispatcher\GenericEvent;

public function onNodeCompleted(GenericEvent $event): void {
    $nodeId = $event->getArgument('node_id');
    $result = $event->getArgument('result');
}

// After
use Drupal\flowdrop_runtime\Event\NodeCompletedEvent;

public function onNodeCompleted(NodeCompletedEvent $event): void {
    $nodeId = $event->nodeId;
    $result = $event->result;
}
```

**Event name strings are unchanged** — if you subscribe using string event names (e.g., `flowdrop_runtime.node.completed`), those still work. Each event class also provides a `NAME` constant (e.g., `NodeCompletedEvent::NAME`).

**Complete list of new event classes:**

| Module | Event Class | Event Name |
|---|---|---|
| `flowdrop_interrupt` | `InterruptCancelledEvent` | `flowdrop_interrupt.cancelled` |
| `flowdrop_interrupt` | `InterruptCreatedEvent` | `flowdrop_interrupt.created` |
| `flowdrop_interrupt` | `InterruptExpiredEvent` | `flowdrop_interrupt.expired` |
| `flowdrop_interrupt` | `InterruptResolvedEvent` | `flowdrop_interrupt.resolved` |
| `flowdrop_pipeline` | `JobCreatedEvent` | `flowdrop.job.created` |
| `flowdrop_pipeline` | `PipelineJobsGeneratedEvent` | `flowdrop.pipeline.jobs_generated` |
| `flowdrop_runtime` | `BatchExecutionErrorEvent` | `flowdrop_runtime.execution.batch_error` |
| `flowdrop_runtime` | `ExecutionErrorEvent` | `flowdrop_runtime.execution.error` |
| `flowdrop_runtime` | `MonitoringErrorEvent` | `flowdrop_runtime.monitoring.error` |
| `flowdrop_runtime` | `MonitoringNodeExecutionEvent` | `flowdrop_runtime.monitoring.node_execution` |
| `flowdrop_runtime` | `MonitoringStartedEvent` | `flowdrop_runtime.monitoring.started` |
| `flowdrop_runtime` | `MonitoringStoppedEvent` | `flowdrop_runtime.monitoring.stopped` |
| `flowdrop_runtime` | `MonitoringWarningEvent` | `flowdrop_runtime.monitoring.warning` |
| `flowdrop_runtime` | `NodeCompletedEvent` | `flowdrop_runtime.node.completed` |
| `flowdrop_runtime` | `NodePostExecuteEvent` | `flowdrop.node.post_execute` |
| `flowdrop_runtime` | `NodePreExecuteEvent` | `flowdrop.node.pre_execute` |
| `flowdrop_runtime` | `NodeSkippedEvent` | `flowdrop.node.skipped` |
| `flowdrop_runtime` | `PipelineCreatedEvent` | `flowdrop.pipeline.created` |
| `flowdrop_runtime` | `RealTimeBroadcastEvent` | `flowdrop_runtime.real_time_event` |
| `flowdrop_runtime` | `WorkflowCompletedEvent` | `flowdrop.workflow.completed` |
| `flowdrop_runtime` | `WorkflowInterruptedEvent` | `flowdrop.workflow.interrupted` |
| `flowdrop_stategraph` | `ApprovalGateApprovedEvent` | `flowdrop_stategraph.approval_approved` |
| `flowdrop_stategraph` | `ApprovalGateRejectedEvent` | `flowdrop_stategraph.approval_rejected` |
| `flowdrop_stategraph` | `ApprovalGateRequestedEvent` | `flowdrop_stategraph.approval_requested` |
| `flowdrop_trigger` | `TriggerPostExecuteEvent` | `flowdrop_trigger.post_execute` |
| `flowdrop_trigger` | `TriggerPreExecuteEvent` | `flowdrop_trigger.pre_execute` |

### Internal Services Made Private (Breaking)

32 services across 6 modules have been marked `public: false`. If your custom code accesses any of these via `\Drupal::service()` or `$container->get()`, switch to dependency injection.

**Affected services:**

- `flowdrop_interrupt`: `logger.channel.flowdrop_interrupt`, `flowdrop_interrupt.persistence`, `flowdrop_interrupt.interrupt_resolved_subscriber`, `flowdrop_interrupt.session_interrupt_resolved_subscriber`
- `flowdrop_orchestration`: `flowdrop_orchestration.helper`
- `flowdrop_runtime`: `flowdrop_runtime.execution_logger`, `flowdrop_runtime.status_tracker`, `flowdrop_runtime.event_broadcaster`, `flowdrop_runtime.workflow_compiler`, `flowdrop_runtime.storage.drupal_pipeline`, `flowdrop_runtime.storage.drupal_job`, `flowdrop_runtime.storage.inmemory_pipeline`, `flowdrop_runtime.storage.inmemory_job`, `flowdrop_runtime.parameter_resolver`, `flowdrop_runtime.data_flow_manager`, `flowdrop_runtime.json_path`, `flowdrop_runtime.workflow_state_manager`, `flowdrop_runtime.entity_snapshot_storage`, `flowdrop_runtime.error_handler`, `flowdrop_runtime.execution_monitor`, `flowdrop_runtime.queue.pipeline_execution`, `flowdrop_runtime.queue.job_execution`
- `flowdrop_session`: `flowdrop_session.execution_service`, `flowdrop_session.deferred_message_processor`, `logger.channel.flowdrop_session`
- `flowdrop_stategraph`: `flowdrop_stategraph.state_manager`, `flowdrop_stategraph.checkpointer_factory`, `flowdrop_stategraph.memory_checkpointer`, `flowdrop_stategraph.entity_checkpointer`, `flowdrop_stategraph.conditional_edge_evaluator`, `flowdrop_stategraph.array_iterator_helper`, `flowdrop_stategraph.approval_gate_handler`, `flowdrop_stategraph.orchestrator`

**Migration example:**

```php
// Before (will fail)
$jsonPath = \Drupal::service('flowdrop_runtime.json_path');

// After (use dependency injection)
public function __construct(
    private readonly JsonPathServiceInterface $jsonPath,
) {}
```

### Constructor Signature Changes (Breaking)

Three class constructors were changed. If you subclass any of these, update the `parent::__construct()` call:

| Class | Removed Parameter |
|---|---|
| `ApprovalGateHandler` | `$entityTypeManager` (was first argument) |
| `StateGraphOrchestrator` | `$configFactory` |
| `FlowDropEndpointConfigService` | Entire constructor removed |

### Exception Hierarchy

All module-level exception classes now extend `FlowDropException` (which itself extends `\RuntimeException`). Existing `catch (\RuntimeException $e)` blocks will continue to work. You can now also catch all FlowDrop errors with `catch (FlowDropException $e)`.

`WorkflowValidationException` has been removed (was unused dead code).

### New Typed Constants

Use the new `@api` constant classes instead of hardcoded strings when building workflow edge data:

```php
use Drupal\flowdrop\Constants\PortDirection;
use Drupal\flowdrop\Constants\EdgeKeys;

$edge[EdgeKeys::SOURCE_HANDLE] = PortDirection::OUTPUT;
```

### Post-Update Steps

```bash
drush cr
```

No database updates are required for this release.

---

## 1.0.0-alpha3 to 1.0.0-alpha4

### API Response Standardization

All API endpoints now return a consistent response envelope:

```json
// Success
{ "success": true, "data": { ... } }

// Error
{ "success": false, "error": "Error message" }

// Paginated
{ "success": true, "data": [...], "pagination": { "total": 100, "limit": 50, "offset": 0, "has_more": true } }
```

**Breaking changes:**
- The `message` field has been removed from all API responses. Use `data` for payload and `error` for errors.
- The `code` field has been removed from error responses. Use HTTP status codes instead.
- The `count` field has been removed from list responses. Use `data.length` client-side or the `pagination` object.
- **WorkflowsController**: Pagination moved from HTTP headers (`X-Total-Count`, `X-Page-Size`, `X-Page-Offset`) to the response body `pagination` object.
- **WorkflowsController**: Hardcoded CORS headers (`Access-Control-Allow-*`) removed. Configure CORS at the server/Drupal level instead.
- **PlaygroundApiController**: The `hasMore` pagination field replaced by the standard `pagination` object.
- **SnapshotApiController**: Pagination format standardized to match the new `pagination` object.

All controllers now use `ApiResponseTrait` from `Drupal\flowdrop\Controller\Api`.

### Entity Changes

- **`FlowDropInterruptInterface`** now extends `EntityChangedInterface` in addition to `ContentEntityInterface` and `EntityOwnerInterface`. Code that type-checks against `FlowDropInterruptInterface` is unaffected. Code that relied on the interface *not* extending `EntityChangedInterface` may need updating.
- **`FlowDropInterrupt`** entity definition converted from `@ContentEntityType` annotation to `#[ContentEntityType]` PHP attribute. No behavioral change.
- **`StateCheckpoint`** class renamed to **`FlowDropStateCheckpoint`**. The entity type ID (`state_checkpoint`) and database table are unchanged. Update `use` statements:
  - `Drupal\flowdrop_stategraph\Entity\StateCheckpoint` → `Drupal\flowdrop_stategraph\Entity\FlowDropStateCheckpoint`
  - `Drupal\flowdrop_stategraph\Entity\StateCheckpointInterface` → `Drupal\flowdrop_stategraph\Entity\FlowDropStateCheckpointInterface`
  - `Drupal\flowdrop_stategraph\Access\StateCheckpointAccessControlHandler` → `Drupal\flowdrop_stategraph\Access\FlowDropStateCheckpointAccessControlHandler`

### Frontend Breaking Changes

**Custom event names** — Update all event listeners:

| Old | New |
|-----|-----|
| `schemaform:change` | `flowdrop:schemaform:change` |
| `schemaform:save` | `flowdrop:schemaform:save` |
| `schemaform:cancel` | `flowdrop:schemaform:cancel` |
| `schemaform:init` | `flowdrop:schemaform:init` |
| `fd-plugin-selected` | `flowdrop:plugin-selector:selected` |

**Drupal behavior names** — Update any code that references behaviors directly:

| Old | New |
|-----|-----|
| `Drupal.behaviors.fdPluginSelector` | `Drupal.behaviors.flowdropPluginSelector` |
| `Drupal.behaviors.fdDepNav` | `Drupal.behaviors.flowdropDependencyNavigator` |

**Data attributes** — Update any CSS or JS that targets these attributes:

| Old | New |
|-----|-----|
| `data-fd-plugin-selector` | `data-flowdrop-plugin-selector` |
| `data-fd-initialized` | `data-flowdrop-initialized` |
| `data-fd-target-input` | `data-flowdrop-target-input` |
| `data-fd-plugin-radio` | `data-flowdrop-plugin-radio` |
| `data-fd-dep-nav` | `data-flowdrop-dependency-navigator` |
| `data-fd-collapse-toggle` | `data-flowdrop-collapse-toggle` |
| `data-fd-copy-json` | `data-flowdrop-copy-json` |
| `data-fd-json` | `data-flowdrop-json` |
| `data-schema-form-initialized` | `data-flowdrop-initialized` |

### Schema Form JS API

The non-functional `mountSchemaForm.update()` method has been removed. If your code called `.update()` on a mounted schema form instance, remove those calls. To re-render with new data, destroy the instance and re-mount.

### Orchestrator ID Detection

Hardcoded orchestrator ID checks (e.g., `if ($executorId === 'synchronous_pipeline')`) have been replaced with capability-based detection via `OrchestratorHelper` methods. If your custom code checks orchestrator IDs directly, migrate to using `OrchestratorHelper::supportsCapability()` or similar helper methods.

### Post-Update Steps

```bash
drush cr
```

No database updates are required for this release.

---

## 1.0.0-alpha2 to 1.0.0-alpha3

### Namespace Changes

Several interfaces and DTOs were relocated to resolve circular dependencies and improve module organization. Update `use` statements:

| Old namespace | New namespace |
|---|---|
| `Drupal\flowdrop_runtime\Service\JsonPath\JsonPathServiceInterface` | `Drupal\flowdrop\Service\JsonPath\JsonPathServiceInterface` |
| `Drupal\flowdrop_orchestration\PipelineExecutorInterface` | `Drupal\flowdrop_runtime\Service\Orchestrator\PipelineExecutorInterface` |
| `Drupal\flowdrop_runtime\DTO\Snapshot\WorkflowSnapshot` | `Drupal\flowdrop_orchestration\DTO\Snapshot\WorkflowSnapshot` |
| `Drupal\flowdrop_runtime\DTO\Snapshot\NodeSnapshot` | `Drupal\flowdrop_orchestration\DTO\Snapshot\NodeSnapshot` |
| `Drupal\flowdrop_interrupt\Exception\InterruptExceptionInterface` | `Drupal\flowdrop_orchestration\Exception\InterruptExceptionInterface` |
| `Drupal\flowdrop_stategraph\DTO\Message` | `Drupal\flowdrop_orchestration\DTO\Snapshot\Message` |
| `Drupal\flowdrop_stategraph\DTO\IteratorState` | `Drupal\flowdrop_orchestration\DTO\Snapshot\IteratorState` |
| `Drupal\flowdrop_runtime\Service\Interrupt\DrupalInterruptPersistence` | `Drupal\flowdrop_interrupt\Service\DrupalInterruptPersistence` |

### Service ID Changes

| Old service ID | New service ID |
|---|---|
| `flowdrop_runtime.interrupt_persistence` | `flowdrop_interrupt.persistence` |

### API Changes

- **`FlowDropWorkflowSnapshot::createFromWorkflowSnapshot()`** now requires a second `EntityStorageInterface $storage` parameter. If you call this method directly, update the call site to pass the storage handler.

### Deprecations

- **`FlowDropPipeline::getReadyJobs()`** is deprecated. Use `JobGenerationService::getReadyJobs()` instead. Will be removed in 1.0.0.

### Internal Refactoring (non-breaking for most users)

These changes are non-breaking for users consuming public APIs, but may affect code that depends on internal implementation details:

- **Procedural hooks converted to PHP 8 attribute-based hook classes** across 7 modules (`flowdrop_workflow`, `flowdrop_job`, `flowdrop_pipeline`, `flowdrop_runtime`, `flowdrop_session`, `flowdrop_interrupt`, `flowdrop_playground`). If you have custom code that patches or extends these `.module` hook functions, update to use the new hook class approach.
- **Magic strings replaced with constant classes**: `PipelineStatus`, `JobStatus`, and `Permissions` constants. If you were using the raw string values directly, consider switching to the constants for forward compatibility.
- **`TriggerManager` split** into `EventFilterService`, `OrchestrationRequestBuilder`, and `TriggerExecutor`. The public `TriggerManagerInterface` remains unchanged.
- **`SessionService` split** by extracting `SessionExecutionService`. The public `SessionServiceInterface` remains unchanged.

### Post-Update Steps

```bash
drush cr
```

No database updates are required for this release.

---

## 1.0.0-alpha1 to 1.0.0-alpha2

### Permission System Overhaul

This release replaces flat permission machine names with granular own/any variants for all content entities. **You must reassign permissions after updating.**

#### Content Entity Permission Changes

| Old permission | New permissions |
|---|---|
| `view flowdrop_job` | `view own flowdrop_job`, `view any flowdrop_job` |
| `edit flowdrop_job` | `edit own flowdrop_job`, `edit any flowdrop_job` |
| `delete flowdrop_job` | `delete own flowdrop_job`, `delete any flowdrop_job` |
| `view flowdrop_pipeline` | `view own flowdrop_pipeline`, `view any flowdrop_pipeline` |
| `edit flowdrop_pipeline` | `edit own flowdrop_pipeline`, `edit any flowdrop_pipeline` |
| `delete flowdrop_pipeline` | `delete own flowdrop_pipeline`, `delete any flowdrop_pipeline` |
| `view flowdrop_workflow_snapshot` | `view own flowdrop_workflow_snapshot`, `view any flowdrop_workflow_snapshot` |
| `edit flowdrop_workflow_snapshot` | `edit own flowdrop_workflow_snapshot`, `edit any flowdrop_workflow_snapshot` |
| `delete flowdrop_workflow_snapshot` | `delete own flowdrop_workflow_snapshot`, `delete any flowdrop_workflow_snapshot` |
| `view state_checkpoint` | `view own state_checkpoint`, `view any state_checkpoint` |
| `edit state_checkpoint` | `edit own state_checkpoint`, `edit any state_checkpoint` |
| `delete state_checkpoint` | `delete own state_checkpoint`, `delete any state_checkpoint` |
| `view flowdrop interrupts` | `view own flowdrop_interrupt`, `view any flowdrop_interrupt` |
| `resolve flowdrop interrupts` | `resolve own flowdrop_interrupt`, `resolve any flowdrop_interrupt` |
| `cancel flowdrop interrupts` | `cancel own flowdrop_interrupt`, `cancel any flowdrop_interrupt` |

#### New Config Entity View Permissions

Non-admin users can now view config entities without full admin access. Assign these as needed:

- `view flowdrop_workflow` — View workflows and their details
- `view flowdrop_node_type` — View node types and their details
- `view flowdrop_node_category` — View node categories

#### Super-Admin Bypass

All access control handlers now check `administer flowdrop` as the first bypass. Users with this permission automatically have full access to all entities across all modules.

### Post-Update Steps

```bash
drush cr
```

After clearing cache, navigate to **People > Permissions** (`/admin/people/permissions`) and reassign the new granular permissions to your roles. No database updates are required.

---

## 0.6.x to 1.0.0-alpha1

### Overview

FlowDrop 1.0 introduces significant architectural changes to prepare for a stable release. Two new modules have been extracted, several interfaces and DTOs have moved to new namespaces, and entity type IDs have been renamed for consistency.

### New Module Dependencies

Two new stable modules were extracted and must be enabled:

```bash
drush en flowdrop_session flowdrop_orchestration
drush cr
```

**`flowdrop_session`** — Extracted from `flowdrop_playground`. Provides session and message entities used by the execution engine. Required by `flowdrop_interrupt`, `flowdrop_playground`, and `flowdrop_workflow_executor`.

**`flowdrop_orchestration`** — Extracted from `flowdrop_runtime`. Provides orchestrator interfaces, DTOs, plugin manager, and helper. Required by `flowdrop_runtime`, `flowdrop_trigger`, and `flowdrop_stategraph`.

### Drupal Version Requirement

Drupal 10 is no longer supported. All modules now require `^11.2`.

### Entity Type Renames

| Old entity type ID | New entity type ID |
|---|---|
| `flowdrop_playground_session` | `flowdrop_session` |
| `flowdrop_playground_message` | `flowdrop_session_message` |

Run `drush updatedb` to migrate existing data. If you have custom code referencing these entity type IDs, update them.

### Service ID Changes

| Old service ID | New service ID |
|---|---|
| `flowdrop_playground.service` | `flowdrop_session.service` |
| `flowdrop_runtime.orchestrator_manager` | `flowdrop_orchestration.plugin_manager` |
| `flowdrop_runtime.orchestrator_helper` | `flowdrop_orchestration.helper` |
| `flowdrop_session.interrupt_resolved_subscriber` | `flowdrop_interrupt.session_interrupt_resolved_subscriber` |

### Namespace Changes

If you have custom code that imports any of the following classes, update the `use` statements:

| Old namespace | New namespace |
|---|---|
| `Drupal\flowdrop_runtime\Exception\InterruptExceptionInterface` | `Drupal\flowdrop_orchestration\Exception\InterruptExceptionInterface` |
| `Drupal\flowdrop_runtime\Service\Interrupt\InterruptPersistenceInterface` | `Drupal\flowdrop_interrupt\Service\InterruptPersistenceInterface` |
| `Drupal\flowdrop_runtime\Service\Interrupt\InterruptState` | `Drupal\flowdrop_interrupt\DTO\InterruptState` |
| `Drupal\flowdrop_runtime\Attribute\Orchestrator` | `Drupal\flowdrop_orchestration\Attribute\Orchestrator` |
| `Drupal\flowdrop_runtime\Service\Orchestrator\OrchestratorInterface` | `Drupal\flowdrop_orchestration\Service\Orchestrator\OrchestratorInterface` |
| `Drupal\flowdrop_runtime\Service\Orchestrator\OrchestratorHelperInterface` | `Drupal\flowdrop_orchestration\Service\Orchestrator\OrchestratorHelperInterface` |
| `Drupal\flowdrop_runtime\Service\Orchestrator\OrchestratorPluginManager` | `Drupal\flowdrop_orchestration\Service\Orchestrator\OrchestratorPluginManager` |
| `Drupal\flowdrop_runtime\Plugin\Orchestrator\OrchestratorPluginInterface` | `Drupal\flowdrop_orchestration\Plugin\Orchestrator\OrchestratorPluginInterface` |
| `Drupal\flowdrop_runtime\Plugin\Orchestrator\OrchestratorPluginBase` | `Drupal\flowdrop_orchestration\Plugin\Orchestrator\OrchestratorPluginBase` |
| `Drupal\flowdrop_runtime\DTO\Orchestrator\OrchestrationRequest` | `Drupal\flowdrop_orchestration\DTO\Orchestrator\OrchestrationRequest` |
| `Drupal\flowdrop_runtime\DTO\Orchestrator\OrchestrationResponse` | `Drupal\flowdrop_orchestration\DTO\Orchestrator\OrchestrationResponse` |
| `Drupal\flowdrop_playground\Entity\FlowDropPlaygroundSession*` | `Drupal\flowdrop_session\Entity\FlowDropSession*` |
| `Drupal\flowdrop_playground\Entity\FlowDropPlaygroundMessage*` | `Drupal\flowdrop_session\Entity\FlowDropSessionMessage*` |
| `Drupal\flowdrop_playground\Service\PlaygroundService` | `Drupal\flowdrop_session\Service\SessionService` |
| `Drupal\flowdrop_session\EventSubscriber\InterruptResolvedSubscriber` | `Drupal\flowdrop_interrupt\EventSubscriber\SessionInterruptResolvedSubscriber` |

### Permission Machine Name Changes

| Old permission | New permission |
|---|---|
| `view any flowdrop_playground_session` | `view any flowdrop_session` |
| `view own flowdrop_playground_session` | `view own flowdrop_session` |
| `create flowdrop_playground_session` | `create flowdrop_session` |
| `delete any flowdrop_playground_session` | `delete any flowdrop_session` |
| `delete own flowdrop_playground_session` | `delete own flowdrop_session` |

### Custom Orchestrator Plugins

If you have custom orchestrator plugins, update:

1. Change the `use` statement for the `#[Orchestrator]` attribute from `Drupal\flowdrop_runtime\Attribute\Orchestrator` to `Drupal\flowdrop_orchestration\Attribute\Orchestrator`.
2. Change base class imports from `Drupal\flowdrop_runtime\Plugin\Orchestrator\OrchestratorPluginBase` to `Drupal\flowdrop_orchestration\Plugin\Orchestrator\OrchestratorPluginBase`.
3. Update any `OrchestrationRequest`/`OrchestrationResponse` imports to the `Drupal\flowdrop_orchestration\DTO\Orchestrator\` namespace.

### API Stability Annotations

All PHP classes now carry one of two annotation levels:

- **`@api`** = Stable public API with full semver BC guarantees
- **`@internal`** = No BC promise, may change in any release (implementation details)

See [docs/reference/api/bc-policy.md](docs/reference/api/bc-policy.md) for the full policy.

---

## Pre-1.0 to 1.0.0

### Database Updates

Run the following after updating to the latest codebase:

```bash
drush updatedb -y
# Or via UI: /update.php
```

#### Required Update Hooks

| Module | Hook | Description |
|--------|------|-------------|
| `flowdrop_runtime` | `10001` | Installs `flowdrop_workflow_snapshot` entity type |
| `flowdrop_pipeline` | `10001` | Adds `snapshot_id` field to `flowdrop_pipeline` entity |

### API Changes

#### 1. ValidationResult is Immutable

**Before (BROKEN):**

```php
$result = ValidationResult::success();
$result->addError("field", "Error message"); // Method does not exist!
```

**After:**

```php
// Option 1: Use static factory methods
return ValidationResult::error("field", "Error message");

// Option 2: Collect errors and create at end
$errors = [];
if ($condition) {
    $errors[] = new ValidationError("field", "Error message");
}
return empty($errors) 
    ? ValidationResult::success() 
    : ValidationResult::failure($errors);

// Option 3: Merge multiple results
$result1 = ValidationResult::error("field1", "Error 1");
$result2 = ValidationResult::error("field2", "Error 2");
return $result1->merge($result2);
```

#### 2. Orchestrator Plugin System

**Before (0.4.2 and earlier):**

Orchestrators were discovered via tagged services in `services.yml`.

**After (0.4.3+):**

Orchestrators use PHP 8 attribute-based plugin discovery:

```php
namespace Drupal\my_module\Plugin\Orchestrator;

use Drupal\flowdrop_runtime\Attribute\Orchestrator;
use Drupal\flowdrop_runtime\Plugin\Orchestrator\OrchestratorPluginBase;

#[Orchestrator(
    id: "my_orchestrator",
    label: new TranslatableMarkup("My Orchestrator"),
    description: new TranslatableMarkup("Description here"),
)]
class MyOrchestratorPlugin extends OrchestratorPluginBase {
    public function execute(OrchestrationRequest $request): OrchestrationResponse {
        // Implementation
    }
}
```

#### 3. NodeProcessorInterface::execute() Removed

**Before (0.3.x):**

```php
public function execute(array $params): array;
```

**After (0.4.0+):**

```php
public function process(ParameterBagInterface $params): array;
```

#### 4. WorkflowSnapshot Integration

The snapshot system is now integrated into all orchestrators. If you have custom orchestrators, ensure they:

1. Generate snapshots after execution
2. Save snapshots via `WorkflowStateManager`
3. Include snapshots in response metadata

```php
// In your orchestrator's execute method:
$snapshot = $this->workflowStateManager->generateSnapshot(
    $workflow,
    $executionId,
    $nodeStates,
    $initialInput
);
$snapshot = $this->workflowStateManager->save($snapshot);

return new OrchestrationResponse(
    success: true,
    data: $result,
    metadata: ["snapshot" => $snapshot]
);
```

#### 5. Pipeline-Snapshot Linking

Pipelines now maintain a reference to their associated workflow snapshot:

```php
// Get snapshot from pipeline
$pipeline = $pipelineStorage->load($id);
$snapshot = $pipeline->getSnapshot();

// Set snapshot on pipeline
$pipeline->setSnapshotId($snapshotEntity->id());
$pipeline->save();
```

### Configuration Changes

#### Trigger Configuration

Trigger categories now default to **disabled**. Enable explicitly in settings:

```yaml
# config/install/flowdrop_trigger.settings.yml
triggers:
  entity:
    enabled: true
  form:
    enabled: true
  user:
    enabled: false
```

---

## Version 0.4.x Changes

### 0.4.3

- **BREAKING**: Orchestrator discovery changed from tagged services to plugin system
- All orchestrators now require `#[Orchestrator]` attribute
- Orchestrator plugins must be in `Plugin/Orchestrator` namespace

### 0.4.1

- Trigger categories default to disabled (security improvement)
- TriggerManager now uses DependencySerializationTrait

### 0.4.0

- **BREAKING**: `NodeProcessorInterface::execute()` removed
- Use `process(ParameterBagInterface $params)` instead
- EntityTrigger split from base Trigger class

---

## Version 0.3.x Changes

### 0.3.0

- `flowdrop_ui` module removed, functionality moved to `flowdrop_ui_components`
- New SDC component architecture for UI elements

---

## Upgrade Checklist

### Upgrading from 1.0.0-beta1 to 1.0.0-beta2

- [ ] Replace string literals passed as `$mode` to `WorkflowExecutionServiceInterface::execute()` with the corresponding `WorkflowExecutionMode` enum case (`::Job`, `::JobAsync`, `::JobFireForget`)
- [ ] Update custom event type plugins: change `getCategory()` return type from `string` to `EventCategory` and return an `EventCategory` enum case
- [ ] Update any call sites that compare `getCategory()` against string literals to compare against `EventCategory` cases
- [ ] Clear cache: `drush cr`

### Upgrading from 1.0.0-alpha5 to 1.0.0-beta1

- [ ] Clear cache: `drush cr`

### Upgrading from 1.0.0-alpha4 to 1.0.0-alpha5

- [ ] Update custom event subscribers: change `GenericEvent` type hints to the specific typed event class (see event table above)
- [ ] Update event property access: replace `$event->getArgument('key')` / `$event->getSubject()` with direct `$event->property` access
- [ ] Replace `\Drupal::service()` calls for any of the 32 services made private with dependency injection
- [ ] Update subclasses of `ApprovalGateHandler`, `StateGraphOrchestrator`, or `FlowDropEndpointConfigService` for changed constructor signatures
- [ ] Replace `catch (WorkflowValidationException)` if used (class has been removed)
- [ ] Optionally use new `catch (FlowDropException $e)` to catch all FlowDrop errors
- [ ] Optionally use `PortDirection` and `EdgeKeys` constants instead of hardcoded strings
- [ ] Clear cache: `drush cr`

### Upgrading from 1.0.0-alpha3 to 1.0.0-alpha4

- [ ] Update any JS code consuming API responses to use `{ success, data, error }` envelope (removed `message`, `code`, `count` fields)
- [ ] Update pagination logic: use response body `pagination` object instead of `X-Total-Count` / `X-Page-Size` / `X-Page-Offset` headers
- [ ] Configure CORS at the server/Drupal level (hardcoded CORS headers removed from controllers)
- [ ] Update `use` statements for renamed entity classes (see entity changes table above)
- [ ] Update frontend event listeners: `schemaform:*` → `flowdrop:schemaform:*`, `fd-plugin-selected` → `flowdrop:plugin-selector:selected`
- [ ] Update CSS/JS targeting data attributes: `data-fd-*` → `data-flowdrop-*`
- [ ] Update behavior references: `fdPluginSelector` → `flowdropPluginSelector`, `fdDepNav` → `flowdropDependencyNavigator`
- [ ] Remove any calls to `mountSchemaForm.update()` — destroy and re-mount instead
- [ ] Replace hardcoded orchestrator ID checks with `OrchestratorHelper` capability-based detection
- [ ] Clear cache: `drush cr`

### Upgrading from 1.0.0-alpha2 to 1.0.0-alpha3

- [ ] Update `use` statements for moved interfaces and DTOs (see namespace table above)
- [ ] Update service references from `flowdrop_runtime.interrupt_persistence` to `flowdrop_interrupt.persistence`
- [ ] Update calls to `FlowDropWorkflowSnapshot::createFromWorkflowSnapshot()` to pass `$storage` parameter
- [ ] Replace deprecated `FlowDropPipeline::getReadyJobs()` with `JobGenerationService::getReadyJobs()`
- [ ] If patching procedural hooks, migrate to the new PHP 8 hook class approach
- [ ] Clear cache: `drush cr`

### Upgrading from 1.0.0-alpha1 to 1.0.0-alpha2

- [ ] Clear cache: `drush cr`
- [ ] Reassign permissions at **People > Permissions** — all flat permissions replaced with own/any variants
- [ ] Update custom code referencing old flat permission machine names (see permission table above)
- [ ] Assign new config entity view permissions (`view flowdrop_workflow`, `view flowdrop_node_type`, `view flowdrop_node_category`) as needed

### Upgrading from 0.6.x to 1.0.0-alpha1

- [ ] Enable new modules: `drush en flowdrop_session flowdrop_orchestration`
- [ ] Run database updates: `drush updatedb`
- [ ] Clear cache: `drush cr`
- [ ] Update custom code referencing old entity type IDs (`flowdrop_playground_session` / `flowdrop_playground_message`)
- [ ] Update custom code referencing old service IDs (`flowdrop_playground.service`, `flowdrop_runtime.orchestrator_*`)
- [ ] Update `use` statements for moved interfaces and DTOs (see namespace table above)
- [ ] Update permission references if used in custom code or config
- [ ] Update custom orchestrator plugins to use `flowdrop_orchestration` namespace
- [ ] Review [bc-policy.md](docs/reference/api/bc-policy.md) to understand `@api` / `@internal` annotations

### General upgrade checklist

- [ ] All custom orchestrators use plugin system with `#[Orchestrator]` attribute
- [ ] All custom node processors use `process()` instead of `execute()`
- [ ] ValidationResult usage follows immutable pattern
- [ ] Trigger configuration explicitly enables required categories
- [ ] Database updates run successfully (`drush updatedb`)
- [ ] Cache cleared after upgrade (`drush cr`)

## Getting Help

If you encounter issues during upgrade:

1. Check the [CHANGELOG.md](CHANGELOG.md) for detailed release notes
2. Review module-specific READMEs in `modules/*/README.md`
3. Check technical documentation in `docs/development/`

---

*Last updated: March 2026*
