## 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>
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-dumpthread 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
tsin 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 createon 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: Triagepins 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 perreference/schema.mdDescription 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.
3. Link an existing issue (dedupe: L response)
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-NNNNidentifier 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_KEYin env — both are per-machine hurdles the skill should not depend on. - Direct GraphQL against
api.linear.apphas the same key-management cost and bypasses the Slack thread as the audit trail. - Routing every action through
@Linearin 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.