Files
ComfyUI_frontend/.claude/skills/bug-dump-ingest/reference/linear-api.md
Dante 559922eaa5 feat: add bug-dump-ingest skill (#11460)
## Summary

Adds a new Claude Code skill at `.claude/skills/bug-dump-ingest/` that
syncs the `#bug-dump` Slack channel into Linear as the system of record,
per discussion in [this
thread](https://comfy-organization.slack.com/archives/C075ANWQ8KS/p1776510375473579).

- Primary mode is bulk sync — every ingestable top-level message becomes
a Linear issue in the Frontend Engineering team's Triage state with
labels for area / env / severity / reporter.
- Marks handled messages via the team emoji scheme:
  - `` — ticket created
  - `:pr-open:` — fix PR open
  - `` — needs more context
  - `🔁` — duplicate
- Since the Slack reactions API isn't exposed to the skill, the
machine-readable marker is a thread reply carrying the Linear URL; the
human is prompted to add the visible parent reaction from a batch list
printed at session end.
- Secondary opt-in per-row mode delegates to `red-green-fix` to author a
failing unit + e2e test, then a minimal fix, then a PR.

## Files

- `SKILL.md` — entry point: workflow, classification, verification,
approval flow, Linear integration (MCP / GraphQL / draft fallback), fix
workflow
- `reference/linear-api.md` — GraphQL snippets for teams / states /
labels / issues
- `reference/schema.md` — field-by-field extraction rules
- `reference/examples.md` — seven worked examples from real recent
`#bug-dump` messages
- `reference/verify-commands.md` — cookbook of false-defect verification
commands

## Linear MCP setup

Two supported paths documented in `SKILL.md`:

- Option A: official hosted Linear MCP at `https://mcp.linear.app/sse`
via `claude mcp add`, OAuth-based, no API key.
- Option B: community self-hosted MCP with `LINEAR_API_KEY` in env.

## Test plan

- [ ] Restart Claude Code session after merging so the Linear MCP tools
register
- [ ] Authorize Linear OAuth on first tool call
- [ ] Dry-run the skill against a 48h window of `#bug-dump`, confirm the
approval table renders with all 9 columns
- [ ] File one real ticket end-to-end; verify labels, Triage state,
Slack thread reply, and permalink attachment
- [ ] Add a `` reaction on that parent; re-run and
confirm the message is skipped

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11460-feat-add-bug-dump-ingest-skill-3486d73d3650810094aee3e4ee79eb86)
by [Unito](https://www.unito.io)

---------

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: bymyself <cbyrne@comfy.org>
2026-04-24 07:20:35 +09:00

9.0 KiB

Linear Slack Bot (@Linear) Reference

The skill drives Linear exclusively through the Linear Slack app (@Linear). There is no Linear MCP, no LINEAR_API_KEY, no GraphQL. Every Linear read/write happens as a Slack message that mentions @Linear in the #bug-dump thread, and the Linear Slack app performs the action and posts a reply card containing the issue URL.

Why Slack-only

  • The #bug-dump thread is already the source of truth; keeping the entire lifecycle (report → ticket → PR → resolution) in one thread means Processed Detection can grep the thread instead of a separate registry.
  • No API key rotation, no MCP server install, no OAuth browser flow — works on any machine that already has the Slack MCP configured.
  • The Linear Slack app's reply card (with issue URL, title, status, and assignee) IS the canonical receipt; the skill records its ts in the session log.

Prerequisites (one-time, per workspace)

The Comfy Slack workspace must already have the Linear Slack app installed (it is — that's how humans use @Linear reactions today) and #bug-dump (channel C0A4XMHANP3) must have Linear enabled for the Frontend Engineering team. Nothing else to configure. If a @Linear invocation silently does nothing, the bot isn't present in the channel — surface that to the human rather than re-trying.

Supported operations

Every operation is a mcp__plugin_slack_slack__slack_send_message call with channel_id=C0A4XMHANP3 and thread_ts=<parent-ts>. The text is a natural-language instruction to the Linear bot. Keep the text concise — Linear parses the first line as the command intent.

1. Create an issue from the thread

mcp__plugin_slack_slack__slack_send_message({
  channel_id: "C0A4XMHANP3",
  thread_ts: "<parent-ts>",
  text: "@Linear create\nTeam: Frontend Engineering\nTitle: <title>\nStatus: Triage\nLabels: source:bug-dump, area:<area>, env:<env>, sev:<severity>, reporter:<handle>\n\n<description body>\n\nSource: <slack-permalink>"
})

Rules:

  • Start with @Linear create on its own line — this is the command token the bot keys on.
  • Always specify Team: Frontend Engineering. Without it, the bot falls back to the Slack workspace's default team, which may not be FE.
  • Status: Triage pins the initial workflow state.
  • Labels: — comma-separated. If a label doesn't exist yet in Linear, the bot creates it on first use (verified in Linear workspace settings). Keep the taxonomy exactly as SKILL.md § Label Taxonomy.
  • <description body> — markdown per reference/schema.md Description Template. Use real newlines, not literal \n.
  • End with Source: <slack-permalink> so the Linear issue body links back even if the auto-attachment of the parent message fails.

The Linear bot replies in the same thread with a card that contains:

  • The Linear URL (https://linear.app/comfy-org/issue/FE-NNNN)
  • Status, assignee (initially unassigned), and applied labels
  • A "View in Linear" button

Parse the URL out of the bot's reply text (or attachments). If no card reply appears within ~10s of polling slack_read_thread, treat it as a creation failure — do NOT proceed to the :white_check_mark: confirmation reply.

2. Search existing open issues (dedupe)

mcp__plugin_slack_slack__slack_send_message({
  channel_id: "C0A4XMHANP3",
  thread_ts: "<parent-ts>",
  text: "@Linear search <keyword-1> <keyword-2>\nTeam: Frontend Engineering\nStatus: open"
})

The bot replies with a card listing up to ~5 matching open issues. Parse identifier (FE-NNNN) and URL per row. Treat a hit as a duplicate per SKILL.md § Pre-flight Dedupe Gate § Check 1.

If @Linear search is not supported in the installed Slack app version, fall back to Slack-native search across the #bug-dump thread replies (previous @Linear cards contain title + URL — grep those for the same keywords). Record which path was used in the session log so the human can see dedupe coverage.

mcp__plugin_slack_slack__slack_send_message({
  channel_id: "C0A4XMHANP3",
  thread_ts: "<parent-ts>",
  text: "@Linear link FE-4521"
})

The bot replies with the linked issue card. The skill then posts its own :white_check_mark: Linked to Linear: <URL> confirmation reply (see SKILL.md § Slack Thread Reply).

4. Add labels to an existing issue

mcp__plugin_slack_slack__slack_send_message({
  channel_id: "C0A4XMHANP3",
  thread_ts: "<parent-ts>",
  text: "@Linear FE-4521 add-labels pr-open"
})

Used when an open PR is discovered after ticket creation and the Linear issue should flip to pr-open.

5. Change status

mcp__plugin_slack_slack__slack_send_message({
  channel_id: "C0A4XMHANP3",
  thread_ts: "<parent-ts>",
  text: "@Linear FE-4521 status In Progress"
})

Rarely used by the skill directly — usually status changes come from the red-green-fix PR lifecycle (Linear auto-moves to In Review when a PR references Fixes FE-4521).

Description body template

The text that follows the command headers is rendered verbatim as the Linear issue description (markdown). Use this template — see reference/schema.md for field-by-field extraction notes:

**Reporter:** <slack-display-name>
**Env:** cloud prod / local / electron / ...
**Severity (proposed):** high/medium/low
**Area:** ui / node-system / workflow / cloud / templates

## Repro

1. ...
2. ...

## Expected

...

## Actual

...

## Attachments (in Slack thread)

- image.png (png, 315 KB)
- Screen Recording.mov (mov, 37 MB)

## Source

Slack: <permalink>
Thread summary: <1-3 bullets if thread adds context>

The Slack permalink is load-bearing — it's the canonical route to attachments, reporter, and any follow-up discussion. Do NOT embed Slack file IDs (F0AT...) directly; they're permissioned.

Parsing the bot's reply

After each slack_send_message that mentions @Linear, poll slack_read_thread (with channel_id=C0A4XMHANP3, thread_ts=<parent-ts>) up to 3 times, ~3s apart. Scan replies authored by the Linear Slack app user for:

  • Any https://linear.app/<org>/issue/FE-\d+ URL → capture as the issue URL.
  • The FE-NNNN identifier pattern → capture as the issue identifier.
  • An error phrase (couldn't, failed, not found, no team matched) → treat as failure; surface the full bot text to the human.

Record the bot reply's ts alongside the captured URL and identifier in the session log.

Failure modes & handling

Symptom Likely cause Handling
No bot reply within 10s Linear app not in channel, or bot outage Halt the batch, surface to human, do NOT fabricate a Linear URL. Remaining approved candidates stay queued for re-run.
Bot replies with "no team matched" Team name typo or Linear workspace drift Re-send with the exact team name from the Linear workspace (default: Frontend Engineering). If it still fails, ask the human to verify.
Bot replies with "couldn't parse labels" One of the labels has syntax the bot rejects Drop the offending label, re-send; log the partial-label failure so the human can patch after.
Bot creates the issue but reply lacks the URL Rare bot format change Re-fetch the thread after ~5s; if URL still absent, open Linear search via @Linear search <title> and recover the identifier + URL.
Multiple @Linear replies match (duplicate card) The skill retried without polling first Keep the earliest card's URL; log the extras. Never re-issue @Linear create for the same candidate without confirming the first card failed.

Never retry @Linear create without first running @Linear search for the same title keywords — a duplicate card is worse than an initial failure because the human has to close one of them manually.

Why no direct API path

  • The Linear MCP (official or community) would require either OAuth setup or LINEAR_API_KEY in env — both are per-machine hurdles the skill should not depend on.
  • Direct GraphQL against api.linear.app has the same key-management cost and bypasses the Slack thread as the audit trail.
  • Routing every action through @Linear in the thread gives humans full visibility in the channel (the bot's card is the receipt) and Processed Detection becomes a simple Slack thread read.

If a future need requires capabilities the @Linear Slack app doesn't expose (bulk operations, private field edits, webhooks), stop and surface the limitation to the human rather than quietly adding an API-key path — the "Slack-only" constraint is intentional.