Symphony Terminal States for Issues: Configuration and Usage Guide

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 within the Tracker embedded schema. The terminal_states field uses an array of strings with a hardcoded default list:

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, the code accesses these values at lines 646–647 using:

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:

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 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:

%{
  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 (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).
  • Users can override defaults in 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →