# UpdatePipelineStage

## Permission Scope

stage

## Overview

Updates the mutable fields of an existing stage: name, category, and color. Only provided fields are updated; omitted fields remain unchanged. Position is not updatable through this command -- use ReorderPipelineStage instead.

## Business Rules

- PipelineStage must exist
- PipelineStage's pipeline must not be locked — locked pipelines keep the lane template seeded at creation
- If name is provided, it must be non-empty and unique within the pipeline
- If category is provided, it must be a valid enum value (NOT_STARTED, IN_PROGRESS, DONE, CANCELED)
- Color is optional and can be cleared
- Consumer extension fields (declared via the `fields` extension point) are passed through to the update; module-managed columns (`id`, `pipelineId`, `position`, timestamps) are stripped and cannot be set this way

## Process Flow

```mermaid
flowchart TD
    A[Receive updatePipelineStage input] --> B{PipelineStage exists?}
    B -->|No| C[Return STAGE_NOT_FOUND]
    B -->|Yes| BB{Pipeline is locked?}
    BB -->|Yes| CC[Return PIPELINE_LOCKED]
    BB -->|No| D{Name provided?}
    D -->|Yes| DD{Name non-empty?}
    DD -->|No| FF[Return INVALID_NAME]
    DD -->|Yes| E{Name unique within pipeline?}
    E -->|No| F[Return DUPLICATE_STAGE_NAME]
    E -->|Yes| G{Category provided?}
    D -->|No| G
    G -->|Yes| H{Category valid?}
    H -->|No| I[Return INVALID_CATEGORY]
    H -->|Yes| J[Update stage fields]
    G -->|No| J
    J --> K[Return updated PipelineStage]
```

## External Dependencies

- None

## Error Scenarios

- **STAGE_NOT_FOUND**: Specified stage ID does not exist
- **PIPELINE_LOCKED**: Locked pipeline lanes are fixed to the template seeded at creation and cannot be modified
- **INVALID_NAME**: Name is empty or blank
- **DUPLICATE_STAGE_NAME**: A stage with the same name already exists on the pipeline
- **INVALID_CATEGORY**: Provided category is not a valid enum value

## Test Cases

- updates stage name
- updates stage category
- updates stage color
- passes extension fields through but strips reserved model columns
- returns error when stage not found
- returns error when pipeline is locked
- returns error when name is blank
- returns error when name duplicates another stage on the pipeline
- returns error when category is invalid
