.agents/skills/issue-repro/SKILL.md
Issue pipeline: Step 2 of 3 (Repro). See documentation/dev/issue-pipeline.md.
Systematically reproduce a SkiaSharp issue and produce structured, schema-validated reproduction JSON.
These 3 reads are REQUIRED. Do not proceed to Phase 1 until all three are loaded.
Quick flow:
- Load issue + any prior triage JSON
- Read references: schema-cheatsheet, anti-patterns
- Create brief plan (5-10 lines: strategy, platform, expected outcome)
- Check environment:
docker info,dotnet --version, available simulators- Build repro project and attempt reproduction
- Test multiple SkiaSharp versions (3A reporter's → 3B latest → 3C main)
- Generate JSON → validate → persist
Phase 1 (Fetch) → Phase 2 (Assess) → Phase 3 (Reproduce) → Phase 4 (JSON + Output) → Phase 5 (Validate) → Phase 6 (Persist & Present)
CACHE=".data-cache/repos/mono-SkiaSharp"
[ -d ".data-cache" ] || git worktree add .data-cache docs-data-cache
git -C .data-cache pull --rebase origin docs-data-cache
cat $CACHE/github/items/{number}.json
gh issue view {number} --repo mono/SkiaSharp --json title,body,labels,comments,state,createdAt,closedAt,author$CACHE/ai-triage/{number}.json exists, extract classification.platforms[], evidence.bugSignals, analysis.nextQuestions[], and output.actionability.suggestedReproPlatform as hints (verify independently). The suggestedReproPlatform (linux|macos|windows) indicates which CI runner was selected for reproduction.Read references/bug-categories.md to classify the issue type and determine the reproduction strategy. That file covers bugs (Sections 1–6), enhancements (Section 7), platform parity gaps (Section 8), and documentation issues (Section 9) — including which conclusion values and layer to use for each.
{reporter_version}: exact SkiaSharp NuGet version{reporter_tfm}: target framework (e.g., net10.0){reporter_code}: reproduction code from the issueIf not stated, use the latest stable release. .NET is forward-compatible — net8.0 libraries work on net10.0 apps. Never say "doesn't support .NET X" for newer TFMs.
⚠️ Run dotnet --info in /tmp/skiasharp/repro/{timestamp}/{number}/ (NOT the SkiaSharp repo, which has global.json pinning SDK 8.0). Record SDK version, workload versions, and runtime version. {timestamp} is the current UTC time in yyyyMMdd-HHmmss format.
Also check: Docker (docker --version), Playwright MCP tools, GPU availability, .NET workloads (dotnet workload list). Install missing workloads now — don't wait.
| Priority | Signals | Platform file |
|---|---|---|
| 1 | Blazor, WASM, WebAssembly, SKHtmlCanvas, browser error | platform-wasm-blazor.md |
| 2 | WPF, WinForms, WinUI, UWP | platform-windows-desktop.md |
| 3 | iOS, Android, MAUI, Xamarin | platform-mobile.md |
| 4 | Linux, Docker, container, NativeAssets.Linux | platform-docker-linux.md |
| 5 | (none) | platform-console.md |
All platform files fall back to platform-console.md for core SkiaSharp bugs.
Tie-breaking: If multiple platform signals (e.g., "WASM + WPF"), use the highest priority. If reporter says "works on X, fails on Y", reproduce on Y first, test X in Phase 3D.
Read the selected platform file. Follow its Create → Build → Run → Verify steps, substituting {reporter_version}, {reporter_tfm}, and {reporter_code}.
Output a brief plan before executing (5-10 lines: what platform, what version, what approach).
Read references/anti-patterns.md for the full list. Critical rules:
issue-fix skill's job.reproduced — even if by-design.rm -rf bin/ obj/ between versions.not-reproduced and needs-platform are VALID SUCCESS conclusions. Reporting inability to reproduce is correct behavior, NOT failure. NEVER invent output you did not observe from an actual command execution.binding/, externals/, samples/, source/, tests/, utils/, or any other product source during reproduction. Repro creates NEW test projects in /tmp/skiasharp/repro/{timestamp}/ only ({timestamp} is the current UTC time in yyyyMMdd-HHmmss format). If you find yourself editing SkiaSharp source, you have crossed into fix territory — stop.store_memory. Reproduction produces JSON artifacts, not memories. Storing unverified observations as permanent facts pollutes all future sessions.validate-repro.ps1 (or .py fallback) and see ✅ before persisting. Mentally checking fields is not validation. If the script isn't run, the reproduction is invalid.Intermittent bugs: If results are inconsistent, run 3–5 times. Reproduced ≥1 time → reproduced with note "Intermittent: X/Y runs". Never reproduced after 5 → not-reproduced.
Effort budget: Phases 1–2: ~5 min. Phase 3A: ~15–20 min. Phases 3B–3D: ~10–15 min. Total: ~30–50 min. If stuck after 3+ substantially different approaches, conclude with what you have.
Pre-flight — confirm before reproducing:
- Issue data loaded (cache JSON or GitHub API)
- Prior triage JSON loaded (if exists in
ai-triage/)- Read references/schema-cheatsheet.md for required fields and enums
- Read references/anti-patterns.md — at least the critical rules
- Environment checked:
docker info(if needed),dotnet --version,xcodebuild -showsdks(if iOS)- Created a brief plan (5-10 lines: reproduction strategy, which platform, what you expect)
- Never use
sudo— if a command requires it, find an alternative approach
Overview — you will test up to 4 configurations:
- 3A: Reporter's version on primary platform (always)
- 3B: Latest stable release (always)
- 3C: Main branch source (MANDATORY if 3B still reproduced)
- 3D: Cross-platform verification (conditional — see table below)
🛑 MINIMUM 2 VERSIONS REQUIRED. You must test at least the reporter's version (3A) AND latest stable (3B). Single-version reproductions are incomplete and will fail schema validation. This applies to ALL conclusion types — bugs (
reproduced/not-reproduced) AND enhancements (confirmed/not-confirmed). For enhancements: a feature may exist in one version but not another, or may have been removed. Version testing reveals this.
Follow the platform file from Phase 2.4. For each step, capture:
| Field | Limit |
|---|---|
command | Exact command (redact paths) |
exitCode | 0=success, non-zero=failure |
output | 2KB success, 4KB failure |
filesCreated | Filename + source code content for repro files |
layer | setup / csharp / c-api / native / deployment / investigation |
result | success / failure / wrong-output / skip |
Step result = what actually happened (technical outcome), not whether it was expected. A build that fails is result: "failure" even if that confirms the bug. See references/anti-patterns.md for details.
Note: Use
layer: "investigation"for source-code analysis steps in enhancement confirmations (grep, file reading). See Phase 2.1 for the full enhancement flow.
Push hard. Don't bail early. Only conclude not-reproduced after genuinely exhausting approaches.
⚠️ Clean build required: Create a fresh project directory per version (
/tmp/skiasharp/repro/{timestamp}/{number}-latest/) orrm -rf bin/ obj/before building. Never justsedthe version — stale native binaries produce unreliable results. See references/anti-patterns.md #7.
Use the same platform strategy from 3A with the latest stable SkiaSharp. Record in versionResults.
🛑 Do NOT skip when reproduced on latest. If the bug reproduces on the latest stable release, testing main is MANDATORY — it tells us whether a fix exists but hasn't been released.
[ -d "output/native" ] && ls output/native/ | head -5 || dotnet cake --target=externals-downloadsamples/Basic/<platform>/. Each platform file has a "Main Source Testing (Phase 3C)" section — follow it.Clean up: Revert sample file changes with
git checkout -- samples/.
| Primary result | Run 3D? |
|---|---|
reproduced (platform-specific) | Yes — test alternative platform |
not-reproduced + reporter on different platform | Yes |
reproduced (pure API bug, no platform signals) | Skip — note why |
not-reproduced + same platform as reporter | No |
Time cap: 5 minutes. Default alternative: Docker Linux x64.
| Primary | Best alternative |
|---|---|
| Console macOS | Docker Linux x64 |
| WASM/Blazor | Console on host |
| Docker Linux | Console on host |
| Windows (reported) | Console + Docker Linux |
Test reporter's version only. Derive scope: reproduced on ≥2 platforms → "universal", primary only → "platform-specific/{platform}", skipped → "unknown".
For confirmed/not-confirmed conclusions: Derive scope from your investigation — if the gap exists in ALL platform views → "universal", if the gap is specific to one platform's view (e.g., Blazor only) → "platform-specific/{platform}". Always set scope for confirmed conclusions (schema requires it).
Some bugs live in framework-specific code (MAUI views, WPF handlers, Uno controls) that can't be run in a console app. Use this escalation order — only move to the next level if the previous one fails:
When to simulate: Only when steps 1-4 are impossible (wrong OS, no SDK, no device). Example: a WPF bug on a macOS host with no Windows VM.
How to simulate: Extract the suspect code path into a standalone console app. Model the framework inputs (e.g., resize events, binding updates) and verify the logic path triggers the bug.
Recording: Set reproProject.type: "simulation" in the JSON. The triage's classifiedPlatform vs repro's type: "simulation" shows the gap clearly.
Example: Opus reproduced SKXamlCanvas NRE (#3430) by extracting the resize handler logic into a console app, feeding 73 dimension combinations, and confirming 100% NRE rate when width/height were 0.
Read references/conclusion-guide.md. Key question: did the reported claim hold true?
| Conclusion | When | Issue Types |
|---|---|---|
reproduced | Reported behavior occurred, including wrong/incomplete output (even if by-design) | Bugs |
not-reproduced | Reported behavior did not occur | Bugs |
confirmed | Reporter's claim verified (feature IS missing, docs ARE wrong) | Enhancements, features, docs |
not-confirmed | Reporter's claim not verified (feature exists, docs are correct) | Enhancements, features, docs |
needs-platform / needs-hardware | Requires unavailable platform/hardware | Any |
partial / inconclusive | Partial or ambiguous results | Any |
Write to /tmp/skiasharp/repro/{timestamp}/{number}.json. Use the exact literal path /tmp/skiasharp/repro/{timestamp}/ — do NOT substitute $TMPDIR or any other variable. {timestamp} is the current UTC time in yyyyMMdd-HHmmss format. Run mkdir -p /tmp/skiasharp/repro/{timestamp} first if needed.
Schema: references/repro-schema.json. See references/repro-examples.md for full worked examples.
Key rules (schema enforces the rest):
nullenvironment.dotnetSdkVersion: exact SDK from dotnet --info. Include wasmToolsVersion for WASM.versionResults: include platform field (e.g., "host-macos-arm64", "docker-linux-x64")/Users/{name}/ → $HOME/), tokens, credentials.If reproduction contradicts triage, record in feedback.corrections[]:
{ "source": "triage", "topic": "classification", "upstream": "...", "corrected": "..." }
When conclusion is reproduced, not-reproduced, confirmed, or not-confirmed, generate the output object with actionability, actions, and a proposed response. Skip for blocked conclusions (needs-platform, needs-hardware, partial, inconclusive).
| Scenario | suggestedAction | Confidence |
|---|---|---|
| Reproduced on all versions including latest/main | needs-investigation | 0.90+ |
| Reproduced on reporter's version, fixed on latest | close-as-fixed | 0.85+ |
| Reproduced on reporter's version, fixed on main (unreleased) | keep-open | 0.80+ |
| Not reproduced — likely environment/config issue | request-info | 0.70+ |
| Not reproduced — works on all tested versions | close-as-fixed | 0.75+ |
| Wrong output confirmed | needs-investigation | 0.85+ |
| Reproduced but appears working-as-designed | close-with-docs | 0.70+ |
| Confirmed — feature/docs gap verified | needs-investigation | 0.85+ |
| Not confirmed — feature/docs actually exist | close-with-docs | 0.75+ |
See references/response-guidelines.md for tone, evidence requirements, status thresholds (ready/needs-human-edit/do-not-post), and conclusion-specific templates.
If reproduction testing reveals a workaround (version upgrade, config change, workload install, API alternative), add to output.workarounds[] as simple strings and include in the proposed response body.
When conclusion is not-reproduced or the response asks for more details, populate output.missingInfo[] with specific items needed (e.g., "Exact .NET SDK version", "Full exception stack trace", "Minimal reproduction project"). Reference these in proposedResponse.body.
Use the same action types as triage. Common repro actions:
| Action | When | Risk |
|---|---|---|
update-labels | Add platform labels confirmed by repro, remove incorrect ones | low |
close-issue | Bug fixed in latest + reporter can upgrade | medium |
set-milestone | Bug confirmed, target a release | low |
link-related | Discovered related issue during repro | low |
🛑 PHASE GATE: You CANNOT persist without passing validation. Skipping validation = INVALID reproduction. The task is incomplete.
# Try pwsh first, fall back to python3
pwsh .claude/skills/issue-repro/scripts/validate-repro.ps1 /tmp/skiasharp/repro/{timestamp}/{number}.json \
|| python3 .claude/skills/issue-repro/scripts/validate-repro.py /tmp/skiasharp/repro/{timestamp}/{number}.json
⚠️ NEVER hand-roll your own validation. NEVER assume it passes. RUN THE SCRIPT. If you have not seen ✅ from the validator, DO NOT proceed to Phase 6.
🛑 PHASE GATE: Validator MUST have printed ✅ before proceeding to Phase 6.
Copy the validated JSON to output/ai/ for collection.
pwsh .claude/skills/issue-repro/scripts/persist-repro.ps1 /tmp/skiasharp/repro/{timestamp}/{number}.json
This copies the JSON to output/ai/ mirroring the data-cache structure.
✅ Reproduction: ai-repro/{number}.json
Conclusion: reproduced
Steps: 5 (3 success, 1 failure, 1 skip)
Environment: macOS arm64, SDK 10.0.102
Version results:
SkiaSharp 2.88.9 (reporter): ❌ REPRODUCED
SkiaSharp 3.116.1 (latest): ❌ REPRODUCED
main (source): ✅ not-reproduced