---
name: linear_tracker_ops
description: Run a team's Linear workspace like an operator: triage inbox sweeps with real accept/decline/route decisions, cycle planning against velocity, workflow-state discipline, and scripted GraphQL API workflows for issues, labels, and comments. Triggers on "Linear triage", "cycle planning", "Linear API", "manage issues in Linear", or any request to operate a Linear workspace from the terminal.
---

# Linear Tracker Ops

## Purpose
Make Linear actually do its job: nothing sits in triage unexamined, cycles hold their scope, and issue states reflect reality. This skill is the operator's handbook plus a working CLI for the routine moves.

## Tooling
`bin/linear-ops.py` (stdlib only, no deps) covers the common operations:

```bash
export LINEAR_API_KEY='lin_api_...'          # personal key, never commit it
linear-ops.py auth                            # verify the key
linear-ops.py triage --team ENG               # everything sitting in triage
linear-ops.py my                              # my open assigned issues, priority-ordered
linear-ops.py states --team ENG               # workflow states for the team
linear-ops.py cycle --team ENG                # recent cycles with scope/finished counts
linear-ops.py create --team ENG --title "..." --priority 2 --label bug
linear-ops.py update --issue ENG-123 --state "In Progress"
linear-ops.py comment --issue ENG-123 --body "Verified on staging."
linear-ops.py list --filter '{"priority": {"gte": 2}}'   # raw IssueFilter escape hatch
```

For one-off calls without the CLI, see `references/graphql-queries.md`.

## Auth
Personal API keys are the right credential for operator scripts. Create one in Linear under Settings > Security & access > API keys. Every request goes to `POST https://api.linear.app/graphql` with the key raw in the header:

```
Authorization: lin_api_...
Content-Type: application/json
```

No `Bearer` prefix on personal keys (that prefix is for OAuth access tokens, a different credential). Keep the key in an env var or a secrets manager, never in the repo, never in chat. The key acts with the permissions of the person who created it, so a service account with the narrowest team access beats a personal key when automation runs unattended.

## Workflow

### 1. Triage sweep (daily, ~15 min per team)
`linear-ops.py triage --team ENG` gives you the inbox. Every item gets a decision, not a glance. The full decision tree lives in `references/triage-runbook.md`. The short version:

- Real work: confirm the team, set priority (1 urgent, 2 high, 3 medium, 4 low, 0 none), assign an owner or leave in Todo for cycle planning, attach labels.
- Junk: decline with a comment saying why, or mark as duplicate of the existing issue.
- Not yet: snooze or leave in triage with a note about when it becomes real.
- A triage inbox that never empties is a broken intake, not a busy team. If the same source keeps filing junk, fix the source.

### 2. Cycle planning (1-2 days before cycle start)
`linear-ops.py cycle --team ENG` shows recent cycles with scope vs finished. Checklist in `references/cycle-planning.md`. The short version: pull issues from Backlog into Todo, confirm scope against the team's recent velocity (Linear shows median velocity in Insights), set scope early and hold it. Incomplete issues roll forward by default, so scope creep is a commitment problem, not a tooling problem. Auto-archive keeps old cycles tidy (default keeps 6).

### 3. State hygiene
Run `linear-ops.py states --team ENG` once and keep the list short. The default flow is enough for most teams: Backlog -> Todo -> In Progress -> In Review -> Done, plus Canceled. Signs of rot: more than one issue In Progress per person, issues sitting In Progress with no activity, Done meaning "merged" instead of "shipped". Fix the behavior, not the board.

## Output Contract
- A triage sweep ends with a report: counts per decision (accepted / declined / duplicate / snoozed), plus a list of anything accepted that still lacks an owner or priority.
- A cycle check ends with: active cycle scope vs velocity, unestimated or unassigned issues in scope, and any mid-cycle additions called out explicitly.
- Every `update` or `comment` echoes the resulting issue identifier and state so the caller can verify it landed.

## Operating Rules
1. Triage is a decision gate, not storage. Never file an issue back into triage to "deal with later".
2. One assignee per issue. Collaboration goes in sub-issues.
3. Never move an issue to a terminal state (Done, Canceled) without evidence the work actually happened or was actually dropped. Merging a PR is not Done; shipping is.
4. Priority 1 means drop everything. If more than a handful of issues carry it, the priorities are lies and the sweep should say so.
5. Prefer the CLI's named commands over hand-written GraphQL. Reach for `list --filter` or `references/graphql-queries.md` only when the CLI genuinely lacks the operation.
6. Webhooks (verified recipes in `references/webhooks.md`) are for reactive automation, polling is for audits. Do not build a poll loop when Linear can push.
