--- name: acp-issue-diagnose description: Triage an Adaptive Cover Pro GitHub issue end-to-end — fetch the attached diagnostics file (or take a local path), run a Sonnet-powered diagnosis, and draft a reply ready to post on the issue. Use when the user says "triage issue NNN", "look at issue NNN", "diagnose issue NNN", "draft a response to issue NNN", or hands over a local diagnostics file with "draft an issue reply". --- # ACP Issue Diagnose End-to-end triage workflow for `adaptive-cover-pro` GitHub issues that include a diagnostics dump. This skill **fetches**, **diagnoses (Sonnet)**, and **drafts a reply**. Posting the reply is gated behind explicit user confirmation. For one-off analysis of a JSON file with no GitHub-issue context, use the simpler `acp-diagnose` skill instead. --- ## Pick the Input Mode | User says… | Mode | | ------------------------------------------------------------ | ------------------------------------ | | "issue 285", "#285", a GitHub issue URL | **issue mode** — fetch from GitHub | | A path like `/tmp/foo.json`, "this file", or a paste of JSON | **local mode** — skip the fetch step | If the user says only "diagnose this" with no number, file, or attachment, ask: "Issue number (e.g. 285) or path to a diagnostics file?" --- ## Step 1 — Acquire the Diagnostics File ### Issue mode ```bash gh issue view --json number,title,body,author,state,labels,createdAt,comments ``` Parse the body for attachment URLs. Home Assistant's diagnostics download is a JSON file; users sometimes rename it to `.log`. Match this regex on the body: ``` https://github\.com/user-attachments/files/\d+/[^\s\)]+\.(?:json|log|txt) ``` - **Zero matches** → check the comments array for the same regex; users sometimes attach in a follow-up. - **Still zero** → draft a reply that asks for diagnostics (template in **Output Format → Missing-data reply**) and stop. - **Multiple matches** → use the **latest** one (last in the body, then last in comments). Note the others in the report so the user can re-run against an earlier file if needed. Download to `/tmp/`: ```bash URL="" OUT="/tmp/acp-issue--$(basename "$URL")" gh api "$URL" > "$OUT" # gh handles auth; works for private repos too ``` If `gh api` fails (occasionally happens for the user-attachments host), fall back to: ```bash curl -sSL -H "Authorization: Bearer $(gh auth token)" "$URL" -o "$OUT" ``` Verify it parsed: ```bash python3 -c "import json; json.load(open('$OUT'))" && echo OK ``` If parse fails, the file is likely an HTML error page (auth/redirect). Show the first 200 bytes to the user and stop. ### Local mode Take the path verbatim. If it's a paste, write it to `/tmp/acp-diag-paste.json` first, then validate parse. --- ## Step 2 — Diagnose with Sonnet Dispatch **one** subagent with the Sonnet model. Do not run the analysis in the main thread — the diagnostics file is large and the structured walkthrough is exactly what a delegated subagent is for. ``` Agent( description: "Sonnet diagnosis of ACP diagnostics", subagent_type: "general-purpose", model: "sonnet", prompt: ) ``` ### Subagent prompt template ``` You are diagnosing an Adaptive Cover Pro diagnostics dump for a GitHub issue. Diagnostics file: Issue context (may be empty for local-mode runs): Number: Title: Reporter: <login or "n/a"> ACP version reported in body: <version or "n/a"> HA version reported in body: <version or "n/a"> Cover type reported in body: <type or "n/a"> Reporter's description (verbatim, may be truncated): --- <first ~1500 chars of issue body, describe + reproduction sections preferred> --- Your job: 1. Run the triage engine over the diagnostics file (from the repo root, with the dev virtualenv — the package import pulls Home Assistant): venv/bin/python scripts/triage_json.py <ABSOLUTE_PATH> [--latest-version <X.Y.Z>] That runs the SAME declarative rule table as the in-product Troubleshoot step and prints one line per finding plus a wiki deep link. Do NOT re-implement the checks by hand — the engine owns them. Pass --latest-version when the issue names the newest release so the STALE_VERSION check can fire. 2. Read the diagnostics file (Read tool, or Bash+Python `json.load` for large files) to investigate anything the engine did NOT flag. Remember the offline seam: the download lacks per-entity capabilities and axis requirements, so rules 8a (COVER_NOT_READY) and 13 (COVER_FEATURE_MISMATCH) cannot fire offline — check a suspected capability mismatch by hand. An unexplained symptom is a missing rule row: note it so the maintainer can add one (see the Developer-Triage-Rules wiki). 3. Cross-reference the reporter's described symptom against the diagnostics: - Does the diagnostics state corroborate the symptom? (e.g. "covers don't move" + `gave_up=true`) - Or contradict it? (e.g. "manual override stuck" + `manual_override_state.entries == []`) - Or is the symptom outside what diagnostics can show? (e.g. a UI rendering bug) 4. Identify the most likely root cause(s), ranked. 5. Note any missing information you'd need to confirm — specific entity states, HA logs around a timestamp, repro steps, etc. Output a single JSON object with this shape — no commentary, no markdown fences: { "header": { "integration_version": "...", "cover_type": "...", "last_update_success": true, "last_update_time": "..." }, "critical": ["..."], // empty array if none "warnings": ["..."], "findings": ["..."], // bullet-form, plain English "decision_trace_summary": "Winning handler: X (position: Y%, reason: ...)", "symptom_vs_diagnostics": "corroborated" | "contradicted" | "orthogonal" | "insufficient", "symptom_analysis": "1-3 sentences: how the diagnostics relate to what the reporter described", "root_cause_ranked": [ {"hypothesis": "...", "confidence": "high|medium|low", "evidence": "..."} ], "info_needed": ["..."], // empty array if you have enough "config_suggestions": ["..."], // concrete option changes if applicable "summary": "1-2 sentences for the issue reply" } Keep total output under ~3KB. Be specific — cite handler names, option names, entity IDs. ``` When the subagent returns, parse its JSON. If parsing fails, show the user what came back and ask whether to retry; do not silently fix. --- ## Step 3 — Draft the Reply Use the structured output from Step 2 to assemble a markdown comment. **Voice: first-person ("I"), matching the maintainer's tone in the existing repo.** Tone: direct, technical, friendly. No filler ("Thanks for reporting!"), no emoji unless flagging a critical issue with a single 🔴. ### Reply template ```markdown Looked at the diagnostics — running ACP {version}, cover type `{cover_type}`, last update {ok/FAILED} at {time}. **What I see:** {2–4 bullets of the most relevant findings — pull from `findings` and `decision_trace_summary`. Lead with anything in `critical`.} **Most likely cause:** {`root_cause_ranked[0].hypothesis` with one-sentence evidence. If confidence is low, hedge ("might be"). If multiple hypotheses are roughly tied, list the top two.} **Suggested next step:** {Pull from `config_suggestions` if any. Otherwise: what to try, or what info you need.} {If `info_needed` is non-empty, add:} **To confirm, could you share:** - {bullet per item} ``` ### Critical-finding override If `critical` is non-empty, lead with a single line: ``` 🔴 The diagnostics show {first critical item}. ``` …then the rest of the template. ### Missing-data reply (no diagnostics found) ```markdown I don't see a diagnostics dump on this issue yet — could you attach one? In Home Assistant: Settings → Devices & Services → Adaptive Cover Pro → ⋮ (the row's overflow menu) → Download diagnostics. Drop the resulting `.json` file into a comment here. If you can also include: - A rough timestamp of when the misbehavior happened (so I can correlate against the diagnostics) - The relevant `cover.*` entity ID …that'd let me get to a root cause faster. ``` --- ## Step 4 — Show, Confirm, Optionally Post Always show the drafted reply to the user first. Then ask **one** question: > Post this comment on issue #N, edit it first, or leave it as-is for me to post manually? Only if the user explicitly says "post it" / "yes, post" do you run: ```bash gh issue comment <N> --body "$(cat <<'EOF' <reply markdown> EOF )" ``` ⚠️ **Posting to GitHub is shared state.** Never post without explicit confirmation in the current turn. A prior approval for a different issue does not carry over. Do not add labels, close the issue, or assign. Those are maintainer judgments outside this skill's scope. --- ## Output Format (skill-final report to user) After Step 3 (drafted but not posted), report: ``` ACP issue triage — #<N> "<title>" by @<author> Diagnostics: <path or attachment URL> ACP <version> · <cover_type> · last update <ok|FAILED> Verdict: <one-sentence summary from Sonnet> Confidence: <high|medium|low — from root_cause_ranked[0]> --- DRAFT REPLY --- <full markdown reply> --- END DRAFT --- Post this on #<N>, edit, or leave for manual posting? ``` For local-mode runs (no issue number), drop the `#N` lines and end with: ``` No issue number provided — copy the draft above into your reply manually, or give me an issue number to post it to. ``` --- ## Safety Rules - **No automatic posting.** Always wait for explicit confirmation in the same turn. - **No label/state changes.** This skill comments only. - **Never delete the downloaded diagnostics file** during the session — the user may want to re-run. - **If the diagnostics file is corrupt or HTML**, stop and surface the raw content excerpt; do not fabricate findings. - **If the reporter's symptom contradicts the diagnostics** (`symptom_vs_diagnostics == "contradicted"`), say so plainly in the reply rather than papering over it. The contradiction is itself a useful finding. - **Cost budget:** one Sonnet diagnosis per issue. If the user re-runs after editing the prompt, that's fine; do not auto-loop. --- ## Notes for Future Maintenance - The Sonnet subagent runs `scripts/triage_json.py` — the SAME rule engine as the in-product Troubleshoot step. New checks are added as rule rows in `diagnostics/triage.py` (see the Developer-Triage-Rules wiki), never as prose in a skill file, so both this skill and the offline `acp-diagnose` skill pick them up automatically. - If GitHub changes the user-attachments URL format, update the regex in **Step 1**. The current pattern is `https://github.com/user-attachments/files/<id>/<filename>`. - HA's diagnostics download produces filenames like `config_entry-adaptive_cover_pro-<ULID>.json`. Some reporters rename to `.log` — that's why the regex accepts `.json|.log|.txt`.