# `Choreo.Requirement`
[🔗](https://github.com/code-shoily/choreo/blob/v0.14.1/lib/choreo/requirement.ex#L1)

Requirements traceability diagram builder on top of Yog.

`Choreo.Requirement` models software requirements, the components that
satisfy them, the tests that verify them, and the stakeholders that own
them. It is aimed at software architects who need practical traceability
and risk analysis rather than strict SysML compliance.

## When to use

Use `Choreo.Requirement` when you need to:

  * document feature requirements alongside the services and tests that
    implement them
* find gaps in test coverage or implementation
* propagate risk from high-level requirements down to components
* perform impact analysis before a change

## Node types

  * `:requirement` — a need, feature, or constraint
  * `:component` — a service, module, or other implementation item
  * `:test` — a test or verification activity
  * `:stakeholder` — a person, team, or source of a requirement

## Edge types

  * `:satisfies` — component fulfills a requirement
  * `:verifies` — test proves a requirement
  * `:refines` — child requirement elaborates a parent requirement
  * `:depends` — requirement needs another requirement first
  * `:traces` — generic traceability link
  * `:contains` — parent requirement contains a child requirement
  * `:derives` — requirement is derived from another

## Quick start

    model =
      Choreo.Requirement.new("Auth v2")
      |> Choreo.Requirement.add_requirement(:mfa,
        id: "REQ-001",
        text: "Users must authenticate with MFA",
        risk: :high
      )
      |> Choreo.Requirement.add_component(:auth_service, label: "Auth Service")
      |> Choreo.Requirement.add_test(:mfa_test, label: "MFA login test")
      |> Choreo.Requirement.satisfies(:auth_service, :mfa)
      |> Choreo.Requirement.verifies(:mfa_test, :mfa)

    Choreo.Requirement.to_mermaid(model)
    Choreo.Requirement.to_dot(model)

## Analysis

    # Coverage gaps
    Choreo.Requirement.Analysis.coverage(model)

    # Risk propagation
    Choreo.Requirement.Analysis.high_risk_gaps(model)

    # Impact of changing a component
    Choreo.Requirement.Analysis.impact_of(model, :auth_service)

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2];
    node [shape=ellipse, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0];

    mfa [label="REQ-001\nMFA", fillcolor="#f59e0b", shape="box"];
    auth_service [label="Auth Service", fillcolor="#3b82f6", shape="roundedbox"];
    mfa_test [label="MFA login test", fillcolor="#10b981", shape="stadium"];

    auth_service -> mfa [label="satisfies"];
    mfa_test -> mfa [label="verifies"];
  }
</div>

# `t`

```elixir
@type t() :: %Choreo.Requirement{
  edge_meta: %{optional(Yog.Multi.Graph.edge_id()) =&gt; map()},
  graph: Yog.Multi.Graph.t(),
  name: String.t() | nil
}
```

# `add_component`

```elixir
@spec add_component(t(), Yog.node_id(), keyword()) :: t()
```

Adds a component node.

Components are implementation items (services, modules, libraries) that
satisfy requirements.

## Options

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:type` (`t:String.t/0`) - Free-form type string for components/tests/stakeholders.

* `:docref` (`t:String.t/0`) - Optional documentation reference.

## Examples

    iex> req = Choreo.Requirement.new()
    ...>   |> Choreo.Requirement.add_component(:auth, label: "Auth Service")
    iex> Choreo.Requirement.components(req)
    [:auth]

# `add_requirement`

```elixir
@spec add_requirement(t(), Yog.node_id(), keyword()) :: t()
```

Adds a requirement node.

## Options

* `:id` (`t:String.t/0`) - Required. Human-readable requirement identifier.

* `:text` (`t:String.t/0`) - Required. Requirement description.

* `:risk` - Risk level: :low, :medium, :high, or :critical. The default value is `:medium`.

* `:verification` - Verification method: :analysis, :inspection, :test, or :demonstration. The default value is `:test`.

* `:kind` - Requirement kind: :requirement, :functional, :interface, :performance, :physical, or :design_constraint. The default value is `:requirement`.

## Examples

    iex> req = Choreo.Requirement.new()
    ...>   |> Choreo.Requirement.add_requirement(:mfa,
    ...>     id: "REQ-001",
    ...>     text: "Users must authenticate with MFA",
    ...>     risk: :high
    ...>   )
    iex> Choreo.Requirement.node(req, :mfa).id
    "REQ-001"
    iex> Choreo.Requirement.node(req, :mfa).risk
    :high

# `add_stakeholder`

```elixir
@spec add_stakeholder(t(), Yog.node_id(), keyword()) :: t()
```

Adds a stakeholder node.

Stakeholders are people, teams, or sources of requirements.

## Options

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:type` (`t:String.t/0`) - Free-form type string for components/tests/stakeholders.

* `:docref` (`t:String.t/0`) - Optional documentation reference.

## Examples

    iex> req = Choreo.Requirement.new()
    ...>   |> Choreo.Requirement.add_stakeholder(:security, label: "Security Team")
    iex> Choreo.Requirement.stakeholders(req)
    [:security]

# `add_test`

```elixir
@spec add_test(t(), Yog.node_id(), keyword()) :: t()
```

Adds a test node.

Tests verify requirements.

## Options

* `:label` (`t:String.t/0`) - Display label (defaults to the node id).

* `:type` (`t:String.t/0`) - Free-form type string for components/tests/stakeholders.

* `:docref` (`t:String.t/0`) - Optional documentation reference.

## Examples

    iex> req = Choreo.Requirement.new()
    ...>   |> Choreo.Requirement.add_test(:t1, label: "Login test")
    iex> Choreo.Requirement.tests(req)
    [:t1]

# `components`

```elixir
@spec components(t()) :: [Yog.node_id()]
```

Returns all component node IDs.

# `contains`

```elixir
@spec contains(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a `contains` relationship: a parent requirement contains a child.

# `depends`

```elixir
@spec depends(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a `depends` relationship: a requirement needs another requirement first.

# `derives`

```elixir
@spec derives(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a `derives` relationship: a requirement is derived from another.

# `edges`

```elixir
@spec edges(t()) :: [{Yog.node_id(), Yog.node_id(), number()}]
```

Returns all edges as `{from, to, weight}` tuples.

# `edges_with_meta`

```elixir
@spec edges_with_meta(t()) :: [{Yog.node_id(), Yog.node_id(), number(), map()}]
```

Returns all edges with their metadata as `{from, to, weight, meta}` tuples.

# `name`

```elixir
@spec name(t()) :: String.t() | nil
```

Returns the diagram name, or `nil` if not set.

# `new`

```elixir
@spec new(keyword() | String.t() | nil) :: t()
```

Creates a new empty requirements diagram.

Accepts an optional name string or keyword options.

## Examples

    iex> req = Choreo.Requirement.new("Auth v2")
    iex> req.name
    "Auth v2"
    iex> req.graph.kind
    :directed

# `node`

```elixir
@spec node(t(), Yog.node_id()) :: map() | nil
```

Returns the raw node data for a given node ID.

# `nodes`

```elixir
@spec nodes(t()) :: [Yog.node_id()]
```

Returns all node IDs in the diagram.

# `refines`

```elixir
@spec refines(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a `refines` relationship: a child requirement elaborates a parent.

# `relate`

```elixir
@spec relate(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a custom relationship between two nodes.

## Options

* `:type` (`t:atom/0`) - Relationship type. The default value is `:traces`.

* `:label` (`t:String.t/0`) - Optional edge label used in DOT rendering.

* `:docref` (`t:String.t/0`) - Optional documentation reference.

## Examples

    iex> req = Choreo.Requirement.new()
    ...>   |> Choreo.Requirement.add_component(:auth)
    ...>   |> Choreo.Requirement.add_requirement(:mfa, id: "REQ-001", text: "MFA")
    ...>   |> Choreo.Requirement.relate(:auth, :mfa, type: :implements)
    iex> meta = req.edge_meta |> Map.values() |> List.first()
    iex> meta.type
    :implements

# `requirements`

```elixir
@spec requirements(t()) :: [Yog.node_id()]
```

Returns all requirement node IDs.

# `satisfies`

```elixir
@spec satisfies(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a `satisfies` relationship: a component fulfills a requirement.

## Examples

    iex> req = Choreo.Requirement.new()
    ...>   |> Choreo.Requirement.add_component(:auth)
    ...>   |> Choreo.Requirement.add_requirement(:mfa, id: "REQ-001", text: "MFA")
    ...>   |> Choreo.Requirement.satisfies(:auth, :mfa)
    iex> [{:auth, :mfa, _}] = Choreo.Requirement.edges(req)

# `stakeholders`

```elixir
@spec stakeholders(t()) :: [Yog.node_id()]
```

Returns all stakeholder node IDs.

# `tests`

```elixir
@spec tests(t()) :: [Yog.node_id()]
```

Returns all test node IDs.

# `theme`

```elixir
@spec theme(atom(), keyword()) :: Choreo.Theme.t()
```

Returns a theme for `Choreo.Requirement` diagrams.

# `to_dot`

```elixir
@spec to_dot(t(), keyword()) :: String.t()
```

Renders the requirements diagram to DOT format.

## Options

  * `:theme` — `:default`, `:dark`, `:warm`, `:forest`, `:ocean`, or a
    `Choreo.Theme` struct

## Examples

    iex> req = Choreo.Requirement.new() |> Choreo.Requirement.add_requirement(:a, id: "R1", text: "A")
    iex> dot = Choreo.Requirement.to_dot(req)
    iex> String.contains?(dot, "digraph")
    true

# `to_graph`

```elixir
@spec to_graph(t()) :: Yog.Multi.Graph.t()
```

Returns the raw `Yog.Multi.Graph` struct underpinning the diagram.

# `to_mermaid`

```elixir
@spec to_mermaid(t(), keyword()) :: String.t()
```

Renders the requirements diagram to Mermaid.js `requirementDiagram` syntax.

## Options

  * `:direction` — `:td` (default), `:lr`, `:rl`, `:bt`
  * `:theme` — used only for styling hints where supported

## Examples

    iex> req = Choreo.Requirement.new() |> Choreo.Requirement.add_requirement(:a, id: "R1", text: "A")
    iex> mermaid = Choreo.Requirement.to_mermaid(req)
    iex> String.contains?(mermaid, "requirementDiagram")
    true

# `traces`

```elixir
@spec traces(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a generic `traces` relationship.

# `verifies`

```elixir
@spec verifies(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()
```

Creates a `verifies` relationship: a test proves a requirement.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
