Validate Issue
Shared function to validate and normalize a Linear issue — type label inference, estimate defaulting, complexity derivation, description structure validation, monochrome prefix application, and assignee/project checks.
Validate Issue
Shared validation function called by /new, /go, and any command that needs to ensure a Linear issue meets Definition of Ready (DoR) standards. This is NOT a standalone user-facing command — it is a procedure referenced by other commands.
Scope: This skill covers issue validation and normalization. It is NOT about issue creation (see
create-enriched-issue) or media processing (seeprocess-media).
Inputs
- Issue ID (required) — Linear issue identifier (e.g.,
ORC-455) .linear.json(required) — Must exist in project root. Contains label UUIDs, status UUIDs, team/project/assignee IDs.
Outputs
After running validate-issue, the issue will have:
- A type label (bug, feature, chore, docs, planning, spike)
- An estimate (defaults to M / 3pts if missing)
- A complexity label (trivial, standard, complex)
- A valid description structure (Summary, Acceptance Criteria, Scope headings)
- A monochrome prefix on the title
- An assignee and project (or
awaiting-inputlabel if missing)
Procedure
V1. Read .linear.json
Read .linear.json from project root. Extract:
labels.type.*UUIDs (bug, feature, chore, docs, planning, spike)labels.complexity.*UUIDs (trivial, standard, complex)team.id,assignee.id,projects.*(first value),statuses.backlog
If .linear.json is missing, stop with error: ".linear.json not found in project root."
V2. Fetch the issue
npx tsx tools/scripts/linear-api.ts get-issue <ID>
V3. Determine type label
Read labels.nodes[]. Find first label matching bug, feature, chore, docs, planning, spike.
If no type label found, infer from title prefix:
| Title prefix | Inferred type |
|---|---|
fix: or fix( |
bug |
feat: or feat( |
feature |
chore: or chore( |
chore |
docs: or docs( |
docs |
| No recognized prefix | default to feature |
Apply type label if missing:
npx tsx tools/scripts/linear-api.ts update-issue <ID> --labels <EXISTING_UUIDS>,<TYPE_UUID>
V4. Default estimate
If estimate is null, default to M (3 points):
npx tsx tools/scripts/linear-api.ts update-issue <ID> --estimate 3
V5. Derive complexity label
If no complexity label present, derive from type × estimate:
| Type | XS (1) / S (2) | M (3) / L (5) / XL (8) |
|---|---|---|
| docs | trivial | trivial |
| planning | trivial | trivial |
| spike | trivial | trivial |
| chore | trivial | standard |
| bug | trivial | standard |
| feature | standard | complex |
Apply complexity label:
npx tsx tools/scripts/linear-api.ts update-issue <ID> --labels <ALL_UUIDS>,<COMPLEXITY_UUID>
Important: --labels replaces all labels. Include ALL existing label UUIDs to preserve them.
V6. Validate description structure
Check for #### Summary, #### Acceptance Criteria, #### Scope headings. If any missing, auto-heal: restructure description preserving all existing content, then update:
npx tsx tools/scripts/linear-api.ts update-issue <ID> --description "..."
V7. Apply monochrome prefix
If the title is missing its monochrome prefix, apply it using this map:
| Type | Symbol | Example title |
|---|---|---|
| bug | ✕ | ✕ dashboard crash on null user |
| feature | ★ | ★ add retry logic to webhook handler |
| chore | ⚙ | ⚙ upgrade Tailwind to v4 |
| docs | ☰ | ☰ add architecture decision record |
| planning | ◎ | ◎ map out auth redesign |
| spike | ↯ | ↯ evaluate vector DB options |
Monochrome prefix format: <symbol> <description> — the title contains only the symbol and a plain description. No conventional commit prefix (feat:, fix:, chore:, docs:) in the Linear title. Git commits still use conventional format — the type-to-prefix mapping happens at SHIP time, not in the title.
If the title already starts with the correct symbol, do not modify it.
Update the title:
npx tsx tools/scripts/linear-api.ts update-issue <ID> --title "<prefixed title>"
V8. Check assignee and project
If either assignee or project is null, apply awaiting-input label and STOP. The calling command should handle this failure gracefully.
Error handling
- If
.linear.jsonis missing → stop with error message - If issue fetch fails → retry up to 3 times, then propagate error to caller
- If assignee/project is null → apply
awaiting-inputlabel and return failure status - All other steps are self-healing (apply defaults, auto-fix structure)