{
  "$defs": {
    "CiSource": {
      "description": "Source of CI status (PR/MR vs branch workflow)\n\nSerialized to JSON as \"pr\" or \"branch\" for programmatic consumers.",
      "oneOf": [
        {
          "const": "pr",
          "description": "Pull request or merge request",
          "type": "string"
        },
        {
          "const": "branch",
          "description": "Branch workflow/pipeline (no PR/MR)",
          "type": "string"
        }
      ]
    },
    "Collected": {
      "description": "Fact families whose collection is gated (`--full`, `[list] summary`,\na listed `ci`/`summary` column). Ungated families (working tree, counts,\ndiffs) are always requested. Serialized as-is into the schema-2 JSON\nenvelope's `collected` field, disambiguating \"absent because not\nrequested\".",
      "properties": {
        "ci": {
          "description": "Forge CI/PR data was fetched.",
          "type": "boolean"
        },
        "summary": {
          "description": "LLM branch summaries were generated.",
          "type": "boolean"
        }
      },
      "required": [
        "ci",
        "summary"
      ],
      "type": "object"
    },
    "GitRepoInfo": {
      "description": "Parsed, provider-neutral repository metadata.\n\nThis is the single shape behind the `repo` / `ci.repo` JSON objects of\n`wt list`; serde controls the field rename/skip rules so there is no\nparallel output-only struct.",
      "properties": {
        "host": {
          "description": "Web host for the repository URL.",
          "type": "string"
        },
        "name": {
          "description": "Repository name.",
          "type": "string"
        },
        "owner": {
          "description": "Repository owner, organization, or namespace path.",
          "type": "string"
        },
        "project": {
          "description": "Azure DevOps project name.",
          "type": [
            "string",
            "null"
          ]
        },
        "provider": {
          "$ref": "#/$defs/GitRepoProvider",
          "description": "Forge provider, or [`GitRepoProvider::Unknown`] for parseable URLs whose\nhost and config do not identify a supported provider."
        },
        "remote": {
          "description": "Local git remote this metadata was derived from. Set only for the\ntop-level `repo` of `wt list`; absent for PR/MR-URL-derived metadata\nsuch as `ci.repo`.",
          "type": [
            "string",
            "null"
          ]
        },
        "url": {
          "description": "Repository web URL.",
          "type": "string"
        }
      },
      "required": [
        "url",
        "provider",
        "host",
        "owner",
        "name"
      ],
      "type": "object"
    },
    "GitRepoProvider": {
      "description": "Supported forge providers for repository metadata.",
      "enum": [
        "github",
        "gitlab",
        "gitea",
        "azure-devops",
        "unknown"
      ],
      "type": "string"
    },
    "JsonChanges": {
      "description": "Working-tree change facts.",
      "properties": {
        "conflicted": {
          "description": "Tracked files carry merge conflicts; null while unresolved.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "deleted": {
          "description": "Has deleted files.",
          "type": "boolean"
        },
        "diff": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonDiff"
            },
            {
              "type": "null"
            }
          ],
          "description": "Lines added/deleted vs HEAD; null while unresolved."
        },
        "modified": {
          "description": "Has modified (unstaged) files.",
          "type": "boolean"
        },
        "renamed": {
          "description": "Has renamed files.",
          "type": "boolean"
        },
        "staged": {
          "description": "Has staged files.",
          "type": "boolean"
        },
        "untracked": {
          "description": "Has untracked files.",
          "type": "boolean"
        }
      },
      "required": [
        "staged",
        "modified",
        "untracked",
        "renamed",
        "deleted",
        "conflicted",
        "diff"
      ],
      "type": "object"
    },
    "JsonCheckStatus": {
      "description": "The pipeline outcomes that reach JSON. [`CiStatus`] carries three more\nthat never appear here, because each is reported by the shape of `checks`\nrather than by a value inside it: `Error` makes both `pr` and `checks`\nunknown, `NoCI` makes `checks` absent, and `Conflicts` leaves `status`\nnull (the conflict itself surfaces as `pr.mergeable`). See\n[`pr_and_checks`].",
      "enum": [
        "passed",
        "running",
        "failed"
      ],
      "type": "string"
    },
    "JsonChecks": {
      "description": "CI pipeline facts.",
      "properties": {
        "source": {
          "$ref": "#/$defs/CiSource",
          "description": "`\"pr\"` or `\"branch\"` (branch workflow)."
        },
        "stale": {
          "description": "Local HEAD is not what CI ran against.",
          "type": "boolean"
        },
        "status": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonCheckStatus"
            },
            {
              "type": "null"
            }
          ],
          "description": "Pipeline outcome; null when a conflicts report masked it."
        }
      },
      "required": [
        "status",
        "source",
        "stale"
      ],
      "type": "object"
    },
    "JsonDefaultBranch": {
      "description": "Relation to the default branch — independent facts, not the table's\npriority-collapsed symbol (that lives in `display.state`).",
      "properties": {
        "ahead": {
          "description": "Commits ahead; null while unresolved (and for orphans).",
          "format": "uint",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "behind": {
          "description": "Commits behind; null while unresolved (and for orphans).",
          "format": "uint",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "diff": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonDiff"
            },
            {
              "type": "null"
            }
          ],
          "description": "Lines added/deleted vs the default branch; null while unresolved."
        },
        "integration": {
          "$ref": "#/$defs/Nullable_JsonIntegration",
          "description": "How committed content is integrated; absent when determined\nnot-integrated, null when undetermined (dirty trees skip the\nexpensive checks)."
        },
        "merge_conflicts": {
          "description": "A merge into the default branch would conflict (local `merge-tree`\nsimulation); null while unresolved.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "orphan": {
          "description": "No merge-base with the default branch; null while unresolved.",
          "type": [
            "boolean",
            "null"
          ]
        }
      },
      "required": [
        "ahead",
        "behind",
        "diff",
        "orphan",
        "merge_conflicts"
      ],
      "type": "object"
    },
    "JsonDevServer": {
      "description": "Dev server facts.",
      "properties": {
        "listening": {
          "description": "The URL's port is listening; null while unresolved.",
          "type": [
            "boolean",
            "null"
          ]
        },
        "url": {
          "description": "URL from the project's `list.url` template.",
          "type": "string"
        }
      },
      "required": [
        "url",
        "listening"
      ],
      "type": "object"
    },
    "JsonDiff": {
      "description": "Line diff statistics",
      "properties": {
        "added": {
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "deleted": {
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "added",
        "deleted"
      ],
      "type": "object"
    },
    "JsonDisplay": {
      "description": "Presentation strings. Everything here is a rendering of facts that\nappear elsewhere in the item.",
      "properties": {
        "columns": {
          "additionalProperties": {
            "type": "string"
          },
          "description": "Rendered `[list.custom-columns]` cells keyed by header; empty cells\nomitted.",
          "type": "object"
        },
        "state": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonMainState"
            },
            {
              "type": "null"
            }
          ],
          "description": "The table's collapsed default-branch state (one value per row,\nhighest priority wins); absent when none applies or unresolved."
        },
        "statusline": {
          "description": "Pre-formatted one-line status with ANSI colors, for prompt tools.",
          "type": [
            "string",
            "null"
          ]
        },
        "symbols": {
          "description": "Raw status glyphs without ANSI (e.g. `\"+!⊂\"`).",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "type": "object"
    },
    "JsonHead": {
      "description": "HEAD commit facts.",
      "properties": {
        "committed_at": {
          "description": "Committer time, RFC 3339 UTC; null when not loaded.",
          "type": [
            "string",
            "null"
          ]
        },
        "sha": {
          "description": "Full commit SHA.",
          "type": "string"
        },
        "short_sha": {
          "description": "Abbreviated per `core.abbrev`, auto-extended for ambiguous prefixes.",
          "type": "string"
        },
        "subject": {
          "description": "Commit subject (first line); null when not loaded (e.g. prunable\nworktrees).",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "sha",
        "short_sha",
        "subject",
        "committed_at"
      ],
      "type": "object"
    },
    "JsonIntegration": {
      "description": "Why committed content counts as integrated.",
      "properties": {
        "reason": {
          "$ref": "#/$defs/JsonIntegrationReason",
          "description": "Which check matched."
        }
      },
      "required": [
        "reason"
      ],
      "type": "object"
    },
    "JsonIntegrationReason": {
      "description": "`IntegrationReason` wire values. Schema 2 uses snake_case throughout; the\nenum's own serde rename (kebab-case) is shared with other surfaces, so the\nmapping lives here instead of on the enum.",
      "enum": [
        "same_commit",
        "ancestor",
        "no_added_changes",
        "trees_match",
        "merge_adds_nothing",
        "patch_id_match"
      ],
      "type": "string"
    },
    "JsonItemV2": {
      "description": "One list row: a worktree, a local branch, or a remote-only branch.",
      "properties": {
        "branch": {
          "description": "Branch name; null for a detached-HEAD worktree. Remote rows carry\nthe bare branch name, with the remote in `remote`.",
          "type": [
            "string",
            "null"
          ]
        },
        "checks": {
          "$ref": "#/$defs/Nullable_JsonChecks",
          "description": "CI pipeline state; absent when no CI exists (or CI wasn't\ncollected), null when the forge fetch failed."
        },
        "default_branch": {
          "$ref": "#/$defs/Nullable_JsonDefaultBranch",
          "description": "Relation to the default branch; absent on the default branch itself."
        },
        "dev_server": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonDevServer"
            },
            {
              "type": "null"
            }
          ],
          "description": "Dev server from the project's `list.url` template; absent when not\nconfigured."
        },
        "display": {
          "$ref": "#/$defs/JsonDisplay",
          "description": "Presentation: rendered strings for humans and prompt tools."
        },
        "head": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonHead"
            },
            {
              "type": "null"
            }
          ],
          "description": "HEAD commit; null for unborn branches."
        },
        "pr": {
          "$ref": "#/$defs/Nullable_JsonPr",
          "description": "Open PR/MR; absent when none exists (or CI wasn't collected), null\nwhen the forge fetch failed."
        },
        "remote": {
          "description": "Remote name for remote-only branch rows; absent on local rows.",
          "type": [
            "string",
            "null"
          ]
        },
        "summary": {
          "$ref": "#/$defs/Nullable_string",
          "description": "LLM-generated branch summary; absent when summaries are off or none\nwas produced, null while pending."
        },
        "upstream": {
          "$ref": "#/$defs/Nullable_JsonUpstream",
          "description": "Tracking-branch relation; absent when no upstream is configured,\nnull while unresolved."
        },
        "vars": {
          "additionalProperties": {
            "type": "string"
          },
          "description": "Custom variables stored via `wt config state vars`.",
          "type": "object"
        },
        "worktree": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonWorktreeV2"
            },
            {
              "type": "null"
            }
          ],
          "description": "Worktree facts; absent on branch-only rows."
        }
      },
      "required": [
        "branch",
        "head",
        "display"
      ],
      "type": "object"
    },
    "JsonMainState": {
      "description": "[`MainState`] wire values. `MainState::None` renders as no value at all,\nso it has no variant here — `display.state` is absent instead. The\n`Integrated` payload is dropped: the reason it carries is already reported\nas `default_branch.integration.reason`.",
      "enum": [
        "is_main",
        "would_conflict",
        "empty",
        "same_commit",
        "integrated",
        "orphan",
        "diverged",
        "ahead",
        "behind"
      ],
      "type": "string"
    },
    "JsonOperation": {
      "description": "[`InProgressOperation`] wire values. The domain enum's `strum` names are\nthe same strings, but going through an exhaustive match is what makes a\nsixth variant a compile error here instead of a value that appears in\npublished output with nothing to catch it.",
      "enum": [
        "merge",
        "rebase",
        "cherry_pick",
        "revert",
        "bisect"
      ],
      "type": "string"
    },
    "JsonPr": {
      "description": "Open PR/MR facts.",
      "properties": {
        "mergeable": {
          "description": "Whether the PR merges cleanly into its target: false when the forge\nreports conflicts, null otherwise (the fetch records only the\nconflicted case).",
          "type": [
            "boolean",
            "null"
          ]
        },
        "number": {
          "description": "Forge number; null when the forge reported none.",
          "format": "uint64",
          "minimum": 0,
          "type": [
            "integer",
            "null"
          ]
        },
        "repo": {
          "anyOf": [
            {
              "$ref": "#/$defs/GitRepoInfo"
            },
            {
              "type": "null"
            }
          ],
          "description": "The repository the PR/MR targets (the upstream for fork PRs);\nabsent when the URL doesn't parse."
        },
        "review": {
          "anyOf": [
            {
              "$ref": "#/$defs/ReviewState"
            },
            {
              "type": "null"
            }
          ],
          "description": "Review state; absent when the forge reports no review signal."
        },
        "url": {
          "description": "URL to the PR/MR page; null when the forge reported none.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "number",
        "url",
        "mergeable"
      ],
      "type": "object"
    },
    "JsonReason": {
      "description": "Reason payload for `locked` / `prunable`.",
      "properties": {
        "reason": {
          "description": "Reason git records; null when none was given.",
          "type": [
            "string",
            "null"
          ]
        }
      },
      "required": [
        "reason"
      ],
      "type": "object"
    },
    "JsonRepo": {
      "description": "Repo-wide facts.",
      "properties": {
        "default_branch": {
          "description": "The branch every `default_branch` object measures against; absent\nwhen detection failed.",
          "type": [
            "string",
            "null"
          ]
        },
        "forge": {
          "anyOf": [
            {
              "$ref": "#/$defs/GitRepoInfo"
            },
            {
              "type": "null"
            }
          ],
          "description": "Forge metadata derived from the primary remote; absent when no\nremote URL parses."
        }
      },
      "type": "object"
    },
    "JsonUpstream": {
      "description": "Tracking-branch relation.",
      "properties": {
        "ahead": {
          "description": "Commits ahead of the upstream.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "behind": {
          "description": "Commits behind the upstream.",
          "format": "uint",
          "minimum": 0,
          "type": "integer"
        },
        "branch": {
          "description": "Branch name on the remote; null when only the remote is known.",
          "type": [
            "string",
            "null"
          ]
        },
        "remote": {
          "description": "Remote name.",
          "type": "string"
        }
      },
      "required": [
        "remote",
        "branch",
        "ahead",
        "behind"
      ],
      "type": "object"
    },
    "JsonWorktreeV2": {
      "description": "Worktree facts. Location attributes (`locked`, `prunable`,\n`branch_mismatch`, `duplicate_branch`) are independent fields — unlike\nschema 1's single `state`, they can co-occur.",
      "properties": {
        "branch_mismatch": {
          "description": "The checked-out branch doesn't match the branch this worktree was\ncreated for.",
          "type": "boolean"
        },
        "changes": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonChanges"
            },
            {
              "type": "null"
            }
          ],
          "description": "Working-tree state; null while unresolved."
        },
        "current": {
          "description": "This is the worktree the command ran from.",
          "type": "boolean"
        },
        "detached": {
          "description": "HEAD is detached.",
          "type": "boolean"
        },
        "duplicate_branch": {
          "description": "Another worktree has the same branch checked out.",
          "type": "boolean"
        },
        "locked": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonReason"
            },
            {
              "type": "null"
            }
          ],
          "description": "Present when the worktree is locked."
        },
        "main": {
          "description": "This is the main worktree.",
          "type": "boolean"
        },
        "operation": {
          "$ref": "#/$defs/Nullable_JsonOperation",
          "description": "In-progress operation; absent when none, null while unresolved."
        },
        "path": {
          "description": "Filesystem path.",
          "type": "string"
        },
        "previous": {
          "description": "This was the previous worktree (`wt switch -`).",
          "type": "boolean"
        },
        "prunable": {
          "anyOf": [
            {
              "$ref": "#/$defs/JsonReason"
            },
            {
              "type": "null"
            }
          ],
          "description": "Present when git considers the worktree prunable."
        }
      },
      "required": [
        "path",
        "main",
        "current",
        "previous",
        "detached",
        "branch_mismatch",
        "duplicate_branch",
        "changes"
      ],
      "type": "object"
    },
    "Nullable_JsonChecks": {
      "anyOf": [
        {
          "$ref": "#/$defs/JsonChecks"
        },
        {
          "type": "null"
        }
      ]
    },
    "Nullable_JsonDefaultBranch": {
      "anyOf": [
        {
          "$ref": "#/$defs/JsonDefaultBranch"
        },
        {
          "type": "null"
        }
      ]
    },
    "Nullable_JsonIntegration": {
      "anyOf": [
        {
          "$ref": "#/$defs/JsonIntegration"
        },
        {
          "type": "null"
        }
      ]
    },
    "Nullable_JsonOperation": {
      "anyOf": [
        {
          "$ref": "#/$defs/JsonOperation"
        },
        {
          "type": "null"
        }
      ]
    },
    "Nullable_JsonPr": {
      "anyOf": [
        {
          "$ref": "#/$defs/JsonPr"
        },
        {
          "type": "null"
        }
      ]
    },
    "Nullable_JsonUpstream": {
      "anyOf": [
        {
          "$ref": "#/$defs/JsonUpstream"
        },
        {
          "type": "null"
        }
      ]
    },
    "Nullable_string": {
      "type": [
        "string",
        "null"
      ]
    },
    "ReviewState": {
      "description": "Review state of a PR/MR.\n\nThe vocabulary matches Claude Code's statusline `pr.review_state` field so\nthe two surfaces never disagree on names. A PR with no review signal at all\n(e.g. GitHub's `reviewDecision` is empty on repos without required\nreviewers and no reviews) carries `None` on [`PrStatus`], not `Pending`,\nso unreviewed branches keep their plain CI colors.",
      "oneOf": [
        {
          "enum": [
            "approved",
            "changes_requested",
            "draft"
          ],
          "type": "string"
        },
        {
          "const": "pending",
          "description": "Review is required before merge (e.g. branch protection) but not given yet",
          "type": "string"
        }
      ]
    }
  },
  "$id": "https://worktrunk.dev/schema/list-v2.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "description": "Root envelope.",
  "properties": {
    "collected": {
      "$ref": "#/$defs/Collected",
      "description": "Which gated fact families this run requested."
    },
    "items": {
      "description": "One entry per row, in table order.",
      "items": {
        "$ref": "#/$defs/JsonItemV2"
      },
      "type": "array"
    },
    "repo": {
      "$ref": "#/$defs/JsonRepo",
      "description": "Repo-wide facts, hoisted out of the items."
    },
    "schema": {
      "description": "Output schema version. The unversioned bare-array format is 1.",
      "format": "uint32",
      "minimum": 0,
      "type": "integer"
    }
  },
  "required": [
    "schema",
    "repo",
    "collected",
    "items"
  ],
  "title": "wt list --format=json (schema 2)",
  "type": "object"
}
