# Pipeline

## Description

Pipeline is a named container that owns an ordered set of stages and the items that flow through them. Each pipeline defines its own isolated stage configuration, allowing teams to maintain distinct workflows (e.g., task boards, support queues, request trackers) without interfering with one another. A pipeline follows a simple lifecycle: it starts as ACTIVE and can be archived via DeactivatePipeline, after which no new items may be added. Each pipeline records the user who created it via `createdByUserId` — immutable creation-time audit metadata, consistent with the module's other audit fields (`changedByUserId`, `movedByUserId`, `authorUserId`). The module imposes no organizational scoping or ownership semantics; consumers that need to attribute boards to a company, project, workspace, or responsible owner add their own foreign-key extension fields via `defineModule({ pipeline: { fields } })`.

Three fields carry pipeline-level semantics beyond name/description:

- `pipelineType` — a free-form kind identifier declared by the consuming app (e.g. `TASK_BOARD`, `SUPPORT_QUEUE`). The module is type-agnostic; the allowed vocabulary can be injected via `defineModule({ pipelineTypes })`, and the name-uniqueness rule is scoped per pipelineType.
- `locked` — managed-board flag. When true, the stage configuration is fixed to the template seeded at creation time (`createPipeline`'s `stages` input): stage-mutating commands (create/update/delete/reorder stage) are rejected with PIPELINE_LOCKED, and the name-uniqueness check is skipped because the managing module owns the board's identity.
- `itemNumberSeq` — the last `itemNumber` issued for this pipeline. A monotonically increasing counter consumed by CreatePipelineItem so that numbers are never reused after an item is deleted (a `MAX(itemNumber)` scheme would reissue the highest number). Nullable: it is unset until the first item is created, in which case the next number falls back to `MAX(itemNumber) + 1`.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> ACTIVE: CreatePipeline
    ACTIVE --> ARCHIVED: DeactivatePipeline
```

| Operation | From | To | Command |
|-----------|------|----|---------|
| deactivate | ACTIVE | ARCHIVED | [DeactivatePipeline](../command/DeactivatePipeline.md) |

### Command Definitions

- [CreatePipeline](../command/CreatePipeline.md) - Create a new pipeline in ACTIVE state
- [UpdatePipeline](../command/UpdatePipeline.md) - Update mutable fields (name, description) of an existing pipeline
- [DeactivatePipeline](../command/DeactivatePipeline.md) - Archive an ACTIVE pipeline, preventing new item creation

### Query Definitions

- [ListPipelineStagesByPipeline](../query/ListPipelineStagesByPipeline.md) - List all stages belonging to a pipeline, ordered by position

### Models

- Pipeline

### Invariants

- Pipeline name is required and must be non-empty
- Pipeline name must be unique per pipelineType across the app (skipped for locked pipelines)
- Creator (`createdByUserId`) is required and is immutable after creation; when the optional `userManagement.getUser` dep is wired, the creator must exist in user-management (otherwise the ID is stored as-is)
- Pipelines are always created in ACTIVE state
- Only ACTIVE pipelines accept new items; ARCHIVED pipelines are read-only for item creation
- Archiving an already-ARCHIVED pipeline is idempotent (no error)
- There is no reactivation path; once ARCHIVED, a pipeline stays ARCHIVED
- Locked pipelines reject stage-mutating commands; their stage set is fixed to the template seeded at creation
- `itemNumberSeq` never decreases; deleted items do not free their numbers for reuse

### Relationships

- **Has Many PipelineStage**: A pipeline contains an ordered set of stages via `pipelineId` on PipelineStage
- **Has Many PipelineItem**: A pipeline contains items via `pipelineId` on PipelineItem
- **Has Many PipelineLabel**: A pipeline owns its classification labels via `pipelineId` on PipelineLabel
- **References User**: Each pipeline records its creator via `createdByUserId` from the user-management module
