# Labels

## Overview

Free-form, pipeline-scoped labels that can be attached to any number of items within the same pipeline. Each pipeline owns its own catalog of labels (a label defined on one pipeline cannot be attached to items in another pipeline). An item can carry any number of labels — there is no hard cap at the module layer. Labels are intended for cross-cutting classification that doesn't fit the fixed stage sequence of a pipeline (e.g., `urgent`, `regression`, `frontend`, `tech-debt`).

## Business Purpose

- Allow teams to define their own categorisation taxonomy per pipeline without schema changes
- Enable filtering and reporting on items by ad-hoc dimensions that cut across PipelineStage
- Keep the taxonomy bounded to a pipeline so different pipelines maintain their own vocabularies

## Process Flow

```mermaid
flowchart TD
    A[User defines a label] --> B[CreatePipelineLabel — name, optional color/description]
    B --> C[PipelineLabel appears in the pipeline's label catalog]
    C --> D{Attach to an item?}
    D -->|Yes| E[AttachLabelToPipelineItem — itemId + labelId]
    E --> F[PipelineItem shows the label in its detail view]
    F --> G{Detach?}
    G -->|Yes| H[DetachLabelFromPipelineItem]
    H --> C
    C --> I{Rename / recolor?}
    I -->|Yes| J[UpdatePipelineLabel — propagates to every attached item]
    J --> C
    C --> K{Retire the label?}
    K -->|Yes| L[DeletePipelineLabel — removes label and all join rows]
```

## Scenario Patterns

- A pipeline defines `urgent`, `regression`, and `needs-followup` labels. A single item can carry all three.
- A pipeline defines an `at-risk` label for analytics. Renaming `at-risk` → `high-risk` updates every item carrying it.
- Deleting a stale label `legacy-stack` automatically detaches it from any items that still carried it.
- A user tries to attach a label defined on one pipeline to an item in another pipeline — the attach is rejected because labels are pipeline-scoped.

## Test Cases

- Creating a label on an active pipeline succeeds
- Creating a label on an archived pipeline is rejected
- Creating a label with an empty name is rejected
- Creating two labels with the same name on the same pipeline is rejected
- Updating a label's name to one that collides with another label on the same pipeline is rejected
- Updating a label propagates the change to items that have the label attached (read path)
- Deleting a label removes it from every item it was attached to
- Listing labels by pipeline returns them ordered by name ascending
- Listing labels by item returns only labels attached to that item
- Attaching a label that belongs to a different pipeline than the item is rejected
- Attaching the same label twice to the same item is rejected
- Detaching a label that was never attached is rejected

## Reference Links

- [GitHub Issues — Labels](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels)
- [Linear — Labels](https://linear.app/docs/labels)
