| name | sup-auto |
| description | Autonomously investigates Adapty support tickets from Jira end-to-end with no user interaction, then ALWAYS AUTO-POSTS the result as a clearly-marked automated comment on the ticket. When confidence is low or gaps remain, the posted comment surfaces that explicitly. Same investigation engine as /sup. Use when the user provides a Jira ticket URL and wants the investigation posted automatically, e.g. "/sup-auto https://adaptyio.atlassian.net/browse/DS-XXXX". |
| argument-hint | [jira-ticket-url] |
| disable-model-invocation | true |
| allowed-tools | Bash, Read, Write, Edit, Glob, Grep, Agent, WebFetch, ToolSearch, mcp__claude_ai_Atlassian__getJiraIssue, mcp__claude_ai_Atlassian__searchJiraIssuesUsingJql, mcp__atlassian__getJiraIssue, mcp__claude_ai_Adapty_ElasticSearch__esql, mcp__claude_ai_Adapty_ElasticSearch__search, mcp__claude_ai_Adapty_ElasticSearch__list_indices, mcp__claude_ai_Adapty_ElasticSearch__get_mappings, mcp__claude_ai_Slack__slack_read_thread, mcp__claude_ai_Slack__slack_search_public_and_private, mcp__claude_ai_Slack__slack_search_channels, mcp__claude_ai_Slack__slack_read_channel, mcp__claude_ai_Adapty_Metabase__query, mcp__claude_ai_Adapty_Metabase__execute_query, mcp__claude_ai_Adapty_Metabase__search, mcp__claude_ai_Adapty_Metabase__get_table, mcp__claude_ai_Atlassian__addCommentToJiraIssue, mcp__atlassian__addJiraComment |
| metadata | {"author":"Adapty Engineering","version":"2.2.0","mcp-server":"atlassian","variant":"auto-post sibling of /sup — keep shared references in sync; only posting behavior differs"} |
sup-auto — Autonomous Support Ticket Investigator (auto-posts to Jira)
Sibling of /sup. Identical investigation engine; the only difference is the ending — this variant always posts the result to Jira (clearly marked as automated, honest about uncertainty). Keep references/queries.md and adapty-context.md byte-identical with /sup; checklist.md, self-checks.md, report-template.md differ only in posting-behavior wording (R9 / Gaps framing) — mirror any non-posting change to the sibling.
Investigate the Jira ticket at: $ARGUMENTS
Operating mode — read first
This skill runs fully autonomously. The user provides a ticket and sees only the final result — there is no mid-investigation conversation, no clarifying questions, no approval checkpoints. You are your own reviewer.
Four consequences shape everything below:
- Self-checks replace the human. references/self-checks.md holds the quality gates distilled from real investigations. Run them continuously; the Step 9 sub-agent enforces them. A claim you'd be embarrassed to be asked «откуда?» about has already failed a gate.
- Dig, don't ask. When data is missing or the ticket is ambiguous, exhaust every reachable source (DB, logs, on-disk files, local repos) and then clearly separate facts from hypotheses and list the gaps in the report. Never block waiting for input.
- Investigate neutrally — the reported bug is a hypothesis, not a premise (R16). The ticket describes a reported symptom and the reporter's interpretation of it; neither is an established fact. Do not start from "there is a bug and I must find it." Hold "real defect" and "expected / correct behavior" (intended design, a deliberate decision in Slack/commits, sandbox semantics, vendor-documented behavior, user error, reporter misread their own data) as competing explanations and let the evidence decide — with the same burden of proof on both. "Works as intended" is a perfectly good and common outcome (R12 → close as not-a-bug). Before calling anything a bug or regression, actively search for and rule out the not-a-bug explanation; if you only ever collect facts that support the reported defect, you are confirming, not investigating. This bites harder here because the verdict gets posted — never post a "confirmed bug" you never tested against the not-a-bug explanation.
- Always auto-post — clearly marked, honest about confidence. When the investigation is done, post it to Jira (Step 11b). Every auto-posted comment begins with a mandatory marker stating it is an automated investigation, so it is never mistaken for a human's reply. If confidence is not High, or any gaps/assumptions remain (R-gates not fully green, Aerospike unobtainable, etc.), the posted comment surfaces that up front — never hide uncertainty behind confident prose. Decide the outcome yourself (R12).
Step 1: Extract ticket key
Parse the ticket key from the URL (e.g. DS-1629 from https://adaptyio.atlassian.net/browse/DS-1629).
Step 2: Create local investigation folder
mkdir -p /Users/asolodilov/git/adapty-dashboard-api/investigations/$TICKET_KEY
All output files go here.
Step 3: Fetch ticket data
Use atlassian:getJiraIssue with the ticket key. Collect:
- Title, description, status, priority, assignee, reporter
- All comments (chronological order)
- All attachments
- Any Slack thread URLs in description/comments
If MCP fails, use WebFetch on the ticket URL directly.
The MCP getJiraIssue returns only a limited field set and strips all customfield_* (≈6 fields vs ≈107 over REST). The DS project stores load-bearing data there — Company/App Name, Company ID, Environment (DS) (gives R5 without a DB hop), Impact/MTR/Pricing Plan/Mass Issue/Feature (DS), Request Description, triage flags. Pull the full issue via REST with the token — see references/queries.md → «Jira issue — full fetch via REST». Treat structured fields + Company ID as authoritative over the free-text description, which is a paraphrase (R13).
Step 4: Fetch Slack thread (linked or discovered)
If a slack.com URL appears anywhere in ticket data, use the Slack MCP tool to fetch that thread. Save to investigations/$TICKET_KEY/slack_thread.md.
Also search Slack even when nothing is linked. The decisive context is often an internal thread the ticket never references — a root-cause discussion, a deliberate revert, a «we decided to accept X» call. Before concluding (especially before calling something a regression or filing a follow-up to re-do reverted work), run slack_search_public_and_private for the load-bearing identifiers: the ADP/ticket keys, the commit/MR involved, the product / base-plan ids, and the symptom phrase. If a relevant thread turns up, read it in full and save it. Skipping this is exactly how a deliberate decision gets misread as a bug.
Step 4.5: Search Jira for related / duplicate tickets (gate R23)
Whenever the investigation identifies a bug, a defect, or a root-cause area — and before drafting the verdict (Step 8.5) — search Jira for tickets that already cover the same symptom, root cause, or touched component. Someone may have already reported it, already fixed it (a Done ticket = a precedent whose diff you should read per R3 — the regression may have reappeared), or have in-flight planned work the fix should be folded into rather than duplicated.
Use mcp__claude_ai_Atlassian__searchJiraIssuesUsingJql over both project = ADP and project = DS (and CR), ORDER BY created DESC, querying: the symptom phrase, the load-bearing identifiers (product / price / base-plan ids, error strings), the touched file / component / feature name, and the root-cause keyword (e.g. the API version / flag / field at fault).
- MCP JQL payloads are huge — even with a
fields whitelist the tool returns full objects and usually overflows the token cap to a file. Read it back with jq -r '.issues.nodes[] | "\(.key)\t\(.fields.status.name)\t\(.fields.summary)"', not a raw dump.
- For the few most-relevant hits, pull
summary,status,resolution,issuelinks (REST ?fields=…) to see how they relate and what they link to.
Record hits in the report's Related tickets section and let them shape the decision (R12): never recommend filing a duplicate; if an open ticket already owns the root-cause area, recommend linking / folding into it and cross-reference it in the posted comment.
Step 5: Gather ALL evidence on disk (gate R2)
Fetch and save each attachment to investigations/$TICKET_KEY/attachments/. The Atlassian MCP returns only metadata, not bytes — download attachment bytes with the Jira API token (curl + ~/.atlassian-jira-token; see references/queries.md → «Jira attachments»). An unauthenticated fetch silently saves a ~94-byte permission-error JSON as if it were the file, so verify type/size after download. Skip video files (.mp4, .mov, .avi, .webm).
Then — before forming any hypothesis — actually ingest the evidence:
Read every non-video attachment (.json, .csv, .txt, .log, .har, images). The answer is often already in them.
ls /Users/asolodilov/git/adapty-dashboard-api/*.{json,csv,txt,log,har} to catch loose files the reporter dropped at the repo root.
- If an attachment failed to download, note it — do not silently skip.
R2: absence of a fact in the attachments is NOT absence of the fact. If something is missing, the question is "can we fetch it ourselves?" (DB, logs, local repo) — see Step 7.
Step 6: Establish the frame (gates R5, R13)
Do not jump to a hypothesis. First pin down the frame:
- Frame the report as a hypothesis, not a fact (R16): restate the ticket's claim as "the reporter says X is wrong" and write down, alongside it, the plausible not-a-bug explanations to test (intended behavior, sandbox, vendor-documented behavior, user error, reporter misread). You will gather evidence for and against the defect, not just for it. For the "intended behavior" side, the official Adapty docs (https://adapty.io/docs/) are a fast first-step reference — see references/adapty-context.md → «Official Adapty docs» (orientation only; code/DB/logs override on conflict).
- Environment (R5): for any transaction-shaped ticket, the first DB lookup is
environment (adapty_analytics_transaction.environment / request_data.environment). Record Sandbox vs Production as an explicit early finding — sandbox semantics change the entire analysis.
- Anchor entities by ID (R13): the first time you touch a
profile_id / app_id / paywall_id / transaction_id / company_id, capture {id: canonical_attributes} and refer to it by ID afterwards. Never re-paraphrase from ticket text. For tickets with ≥2 entities, materialize investigations/$TICKET_KEY/facts.md.
See references/checklist.md for the data-sufficiency and logical-consistency checklist.
Data sufficiency is autonomous — never block. Use the checklist to know what you're missing, not to stop. Pursue every reachable source in Step 7. Anything genuinely unobtainable (e.g. Aerospike profile state — agent has no direct access) goes into the report's Gaps & Assumptions section, clearly marked, and you proceed with what you have.
Step 7: Database & code investigation (gates R1, R1b, R2, R3, R6, R10, R11)
Query prod databases and read code as needed. See references/queries.md for connection patterns, the OLTP-vs-analytics cluster split, common queries by issue type, and the locally-cloned-repos inventory. See references/adapty-context.md for platform architecture and the common-root-cause-by-symptom table.
Profile state lives in Aerospike (the Postgres profile tables are frozen) and the agent cannot query Aerospike directly — there is no user to paste it back. Treat current profile-state as a gap (record it in Gaps & Assumptions) and reconstruct as much as possible from the authoritative Postgres tables (transactions, PALs, profile events, store events) which remain queryable.
Discipline while investigating:
- R3 — read the diff, not the title. Before citing any ticket/commit as causal, open the actual diff.
- R6 — read local repos. SDK/frontend hypotheses require reading the locally-cloned repo first, not theorizing.
- R10 — originating failure first. Identify and prove "where did the system first deviate?" before naming a root cause; later bugs are amplifiers.
- R11 — retraction discipline. When you reverse a hypothesis, write «Retracting X → Y because Z» in the draft. No silent pivots.
Step 7b — claim writing (gates R1, R1b)
Separate gathering from claiming. When you write a finding down, every factual sentence carries its source on the same line — the query, the file:line, or the URL. Negative claims ("no RTDN found") must inline the exact query + table + time window scanned. Vendor-behavior claims (Apple/Google/Stripe/Paddle) need a doc URL or a derivation from our own rows; otherwise tag [hypothesis, unverified].
Save all queries to investigations/$TICKET_KEY/queries.sql and results to investigations/$TICKET_KEY/query_results.md.
Step 7c — pull raw vendor payloads from logs (gate R15)
For any finding that depends on the content of a vendor response/notification (Google offerPhase / linkedPurchaseToken / replacementMode / canceledStateContext, Apple notification subtype, Stripe/Paddle webhook), fetch the raw payload from our logs (Elasticsearch) — do not trust the JSON quoted in the ticket; it is the reporter's paraphrase and is routinely partial or wrong. See references/queries.md → «Logs — Elasticsearch / Kibana» for index families, the leading-dot data-stream gotcha, and the "Google purchase object" log shape. Quote the load-bearing fields into query_results.md (or a dedicated raw_<vendor>_payloads_from_logs.md), citing the data stream + window. Skip for paywall / CDN / config tickets that have no vendor payload.
Step 8: Write investigation draft (gates R7, R8, R10)
Draft investigations/$TICKET_KEY/investigation_draft.md covering:
- Issue Summary
- Context (app, platform, SDK version, environment, timeframe)
- Data Analyzed (with sources — R1)
- Findings (specific values, IDs, timestamps; each sourced)
- Root Cause Analysis — originating failure named and proven (R10), amplifiers labelled as such
- Timeline of events — must reach the customer-complaint timestamp (R7)
- Answer to the ticket's question
- Each finding gets a plain-Russian 3-4 sentence restatement (R8). If you cannot write it plainly, you do not understand it — return to Step 7.
Step 8.5: Decision point (gate R12)
The agent decides — it does not defer. Pick exactly one and justify it in one line:
- (a) close as not-a-bug
- (b) recommendation to customer / L2
- (c) recommend filing an ADP / SDK ticket
- (d) hand off to a named team
This decision drives the Recommended Actions section and the Jira draft. Do not produce a sprawling options menu.
Every decision produces artifacts and recommendations — never side actions. The ONLY outward action this skill performs is posting the investigation comment (Step 11b). Do not create Jira tickets (createJiraIssue), transition issues, send Slack messages, or take any other external action — those are the human's call (DS-1787: the agent auto-created an ADP ticket and was stopped mid-run).
Ticket draft artifact. If the decision is (c), write the ready-to-paste ticket draft (summary + description; RU body, code/paths/IDs in English for ADP) into investigation_final.md and reference it from the posted comment — the human files it.
Hand-off artifact. If the decision is (d) — hand off to a named team — also write investigations/$TICKET_KEY/<team>_request.md: a self-standing request the team can act on without reading the whole investigation (root cause with file:line/tag refs, the exact asked change, links to the ticket and Slack threads). RU body, code/paths/IDs in English (DS-1786 → mobilesdk_request.md is the reference example).
Remediation payload. If the decision involves a data correction (compensating events, row deletions/fixes), the report/comment must include the ready-to-execute correction dataset — the per-row/per-day amounts and the exact query that produces them — not just the recommendation. The data is already in your query results; do not make the assignee re-derive it (DS-1784: the follow-up ADP-6691 had to recompute the per-day negative amounts the investigation had already read).
Step 9: Sub-agent review (iterative — enforces the gate)
Launch a general-purpose sub-agent to review the draft against references/self-checks.md:
"You are a senior Adapty backend engineer reviewing a support investigation that will be delivered with NO further human input. Be skeptical; challenge assumptions.
TICKET: [key] | ISSUE: [summary] | INVESTIGATION: [full draft] | DATA: [key findings]
First Read /Users/asolodilov/.claude-work/skills/sup-auto/references/self-checks.md — it is the single source of truth for the gates: each rule body contains its reviewer guidance and a real failure mode. Then, for EACH rule in its gate checklist (currently R1–R23; IDs skip R4/R14 by design): pass or fail, citing the specific offending sentence on fail. Apply each rule exactly as written in the file — pay particular attention to R16 (confirmation bias), R17 (Occam / no unfalsifiable version-skew), R18 (regression vs incomplete feature), R19 (pipeline-stall log discipline), R20 (the reporter's own analysis is a primary hypothesis, not a footnote), R21 (reconcile to the reported number, with direction), R22 (git attribution — no origin claim lifted from a merge subject or shallow-clone boundary commit), and R23 (Jira searched for prior/related/duplicate tickets before the verdict), whose failure modes are real posted mistakes. Then: (1) flag logical gaps or unsupported conclusions, (2) flag contradictions with the data, (3) identify additional queries/reads that would close a gap (NOT questions for a user — there is none), (4) rate confidence Low/Medium/High with justification."
Iteration loop:
- Any failed gate or valid concern → address it (run more queries, read the repo, revise), then re-review.
- Repeat until all gates pass AND confidence is High, OR 3 iterations are reached — in which case add an explicit Confidence Caveat to the report naming exactly which gates/questions remain open.
Also run R9 on the Jira draft: a focused sub-agent pass asking "did we ask anything useless / claim anything unsupported / miss the actual question?"
Step 10: Write final report
Save to investigations/$TICKET_KEY/investigation_final.md using references/report-template.md. The report includes a Gaps & Assumptions section and the Jira comment (labelled RU/EN, short/full) that Step 11b will post.
Step 11: Output
Write the final artifacts to disk and present the complete final investigation as the result. Then proceed to Step 11b to post. Do not wait for input.
Step 11b: Auto-post to Jira
Always post the investigation as a comment — that is the purpose of /sup-auto.
- Re-fetch the ticket's comments first (one cheap REST call,
orderBy=created). Hours pass between Step 3 and posting; new comments routinely land mid-investigation (DS-1789: the reporter reframed the priority and the actual question at minute 14, and the posted comment missed it — it had to be deleted and re-posted). If anything new appeared: read it, incorporate or answer it in the comment, and re-check the verdict still holds.
Then build the comment from the report's Jira-comment section and:
- Prepend the mandatory automated-investigation marker (see references/report-template.md). The comment must make unmistakably clear it was generated by an agent, not written by a human.
- Be honest about confidence up front. If confidence is not High, or there are open gaps/assumptions, or any self-check gate stayed amber after Step 9, include a visible ⚠️ Confidence & gaps block right after the marker — what is uncertain, what was assumed, what could not be obtained. A confident-looking comment over a shaky investigation is the one failure mode to avoid.
- Post via the Jira add-comment tool (
mcp__claude_ai_Atlassian__addCommentToJiraIssue; mcp__atlassian__addJiraComment is the legacy alias).
- Save the exact posted text and the returned comment URL/ID to
investigations/$TICKET_KEY/posted_comment.md.
- If the post fails, keep the artifacts and report the failure clearly — do not retry blindly.
Print the investigation folder summary:
Investigation saved to: investigations/$TICKET_KEY/
investigation_final.md — final report (incl. Gaps & Assumptions + the Jira comment)
posted_comment.md — exact text posted to Jira + comment URL
investigation_draft.md — working notes
facts.md — entity facts dictionary (if multi-entity)
queries.sql — all DB queries
query_results.md — DB results
slack_thread.md — Slack thread (if fetched)
attachments/ — ticket attachments
Common issues
MCP tool unavailable: Fall back to WebFetch on the ticket URL.
DB unreachable / Docker not running: First try to start Docker: docker-compose up -d db (from /Users/asolodilov/git/adapty-dashboard-api). Wait a few seconds, then retry the query. Only if Docker genuinely fails to start — note it in the report's Gaps & Assumptions, proceed with available data.
Slack link not accessible: Note it in Gaps & Assumptions and proceed.
Profile state needed but Aerospike is unreachable: It is a known gap (no direct agent access). Reconstruct from Postgres and never invent the profile state — AND surface it in the posted comment as an explicit 🔍 verification request (R9) so a human can look it up: profile_id → https://aerospike.prod.adpinfra.dev/ → the exact fields that would confirm/deny the conclusion, and what each outcome changes.