# Symphony Terminal States for Issues: Configuration and Usage Guide

> Understand Symphony terminal states like Closed, Cancelled, Duplicate, and Done. Learn to configure and use these states effectively for issue management in this comprehensive guide.

- Repository: [OpenAI/symphony](https://github.com/openai/symphony)
- Tags: how-to-guide
- Published: 2026-05-08

---

**Symphony treats five default states—`Closed`, `Cancelled`, `Canceled`, `Duplicate`, and `Done`—as terminal states that indicate an issue is finished and should no longer be considered for further work.**

In the `openai/symphony` repository, terminal states are a core configuration concept that determines when an issue lifecycle ends. These states prevent the orchestrator from reopening, reassiging, or processing issues that have reached completion. By default, Symphony ships with a predefined set of terminal states embedded in the configuration schema, though users can override these values in their application configuration.

## Default Terminal States in Symphony

Symphony defines terminal states as string values that represent irreversible completion statuses. Unless explicitly customized, the system recognizes the following five states as terminal:

- **`Closed`** – The issue has been formally closed and archived.
- **`Cancelled`** – The issue was cancelled before completion (Commonwealth spelling).
- **`Canceled`** – Alternative spelling of "Cancelled" (American spelling).
- **`Duplicate`** – The issue duplicates another existing issue.
- **`Done`** – The work associated with the issue is complete.

These defaults ensure compatibility with common project management workflows while accounting for spelling variations across different English locales.

## Configuration Schema Location

The default terminal states are declared in **[`elixir/lib/symphony_elixir/config/schema.ex`](https://github.com/openai/symphony/blob/main/elixir/lib/symphony_elixir/config/schema.ex)** within the `Tracker` embedded schema. The `terminal_states` field uses an array of strings with a hardcoded default list:

```elixir
field(:terminal_states, {:array, :string},
  default: ["Closed", "Cancelled", "Canceled", "Duplicate", "Done"])

```

This schema definition (lines 53–55) establishes the baseline configuration that the application uses when no user overrides exist. The `SymphonyElixir.Config.Schema` module validates these entries during application startup, ensuring all terminal states are properly formatted strings.

## Runtime Access and Usage

During execution, the orchestrator retrieves the configured terminal states via the runtime configuration system. In **[`elixir/lib/symphony_elixir/orchestrator.ex`](https://github.com/openai/symphony/blob/main/elixir/lib/symphony_elixir/orchestrator.ex)**, the code accesses these values at lines 646–647 using:

```elixir
Config.settings!().tracker.terminal_states

```

Internally, the orchestrator likely implements a `terminal_state_set/0` function (as referenced in the source) that converts this list into a `MapSet` for O(1) membership testing. This pattern allows efficient filtering when processing large batches of issues:

```elixir
terminal_states = Config.settings!().tracker.terminal_states
if MapSet.member?(MapSet.new(terminal_states), normalize_issue_state(issue.state)) do
  Logger.info("Issue is terminal – no further processing")
end

```

## Customizing Terminal States

Users can override the default terminal states by specifying a custom list in their [`config.exs`](https://github.com/openai/symphony/blob/main/config.exs) or runtime configuration. This customization occurs under the `:tracker` key, alongside other tracker-specific settings like `active_states` and `project_slug`.

To customize, define your preferred terminal states in the configuration map:

```elixir
%{
  tracker: %{
    kind: "linear",
    api_key: "$LINEAR_API_KEY",
    project_slug: "my-project",
    active_states: ["Todo", "In Progress"],
    terminal_states: ["Closed", "Cancelled", "Done", "Won't Fix"]
  }
}

```

When overriding, you must include all desired terminal states explicitly—the configuration does not merge with defaults. Any state not listed in `terminal_states` will be treated as active and eligible for processing by the orchestrator.

## Summary

- Symphony defines **five default terminal states**: `Closed`, `Cancelled`, `Canceled`, `Duplicate`, and `Done`.
- Defaults are hardcoded in **[`elixir/lib/symphony_elixir/config/schema.ex`](https://github.com/openai/symphony/blob/main/elixir/lib/symphony_elixir/config/schema.ex)** (lines 53–55) within the `Tracker` embedded schema.
- The orchestrator accesses these states at runtime via **`Config.settings!().tracker.terminal_states`** (lines 646–647 in [`orchestrator.ex`](https://github.com/openai/symphony/blob/main/orchestrator.ex)).
- Users can override defaults in [`config.exs`](https://github.com/openai/symphony/blob/main/config.exs) by specifying a custom `terminal_states` array under the `:tracker` configuration key.
- Terminal states prevent further processing, while all other states are considered active and eligible for workflow operations.

## Frequently Asked Questions

### Can I add custom terminal states like "Won't Fix" or "Resolved"?

Yes. You can define any custom terminal states in your configuration file by listing them in the `terminal_states` array under the `:tracker` key. When overriding defaults, include all states you want treated as terminal—both standard and custom—as the configuration system uses your explicit list rather than merging with built-in defaults.

### What is the difference between `active_states` and `terminal_states` in Symphony?

**`active_states`** defines which issue states indicate work is currently in progress or ready to be started, while **`terminal_states`** identifies when work has permanently ended. The orchestrator uses these lists to filter issues: it processes active states and ignores terminal states. Both configurations are optional, but defining them explicitly prevents ambiguity in workflow automation.

### Why does Symphony include both "Cancelled" and "Canceled" as defaults?

The configuration includes both spellings—Commonwealth ("Cancelled") and American ("Canceled")—to maximize compatibility with external issue trackers like Linear, GitHub, or Jira that might use either spelling convention. This prevents issues from being incorrectly classified as active simply due to regional spelling differences. If your organization standardizes on one spelling, you can override the defaults to include only your preferred variant.

### How does Symphony normalize issue state names when checking against terminal states?

According to the source code patterns, Symphony likely implements a `normalize_issue_state/1` function that standardizes casing and formatting before checking membership in the terminal state set. This ensures that states like "closed" or "CLOSED" match against the configured "Closed" value, though the exact normalization logic depends on your specific tracker adapter implementation in the `SymphonyElixir` namespace.