Build-an-X workflow that turns a structured test-run artifact (JUnit XML, Allure JSON, TestRail / Xray / Zephyr export) plus optional release context (version, build URL, deploy target) into a narrative markdown summary for release notes, an exec status update, or a stand-up Slack post. Distinct from the per-framework parsers junit-xml-analysis / allure-reports / coverage-diff-reporter, which emit structured tabular reports: this skill takes the same data and writes the human-readable narrative. Use when a manager needs a draft release note or stand-up summary from a single run; for cross-run trend analytics use currents-integration.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
A test run produces structured data (pass / fail counts, duration, failures with stack traces, coverage). A status update needs narrative (one-paragraph summary, top-3 highlights, single-line status banner). Closing the gap is what 70% of practitioners already do with chat models per PractiTest's 2026 State of Testing Report, but with two known failure modes: hallucinated numbers and inconsistent tone across runs. This skill constrains the output shape so the same input always produces the same structure, removing the variance.
The skill is the manager-layer equivalent of the structured-parser skills. Where junit-xml-analysis produces a parsed report, this skill produces the prose draft a manager edits before pasting into Slack / a release-notes PR / an exec-summary email.
Do not use this skill to:
junit-xml-analysis, allure-reports, or coverage-diff-reporter.status-update) and fill its template from the computed numbers (Output shapes).Accept one of three input shapes:
| Input | Format | Source |
|---|---|---|
| JUnit XML | One or more *.xml files conforming to the JUnit XML schema (testsuite + testcase + failure / error / skipped child elements) | CI runners, surefire, gradle, pytest --junitxml, jest-junit |
| Allure results | Directory of *-result.json + *-container.json per https://allurereport.org/docs/ - Allure organises results by test status (passed / failed / broken / skipped / unknown), categories, and severity levels | allure-pytest, allure-jest, allure-junit5, allure-cucumber, etc. |
| Test-management API export | TestRail run export, Xray run export, Zephyr cycle export | Test-management integration skills |
If multiple inputs are supplied, merge by run-id (or by test-name + start-time if no id) before summarisation. Conflicts in pass/fail status (same test reported as passing in one source and failing in another) are flagged in the output, not silently resolved.
Six metrics anchor every narrative. The skill computes them from the input and never invents:
| Metric | Definition | Why load-bearing |
|---|---|---|
| Total / passed / failed / skipped | Counts per status | The triage line |
| Pass rate | passed / (passed + failed); skipped excluded | The single-number health signal |
| Duration | wall-clock from earliest start to latest end | Tells the reader "is this a 5-minute smoke or a 2-hour regression" |
| Top-N failures | The N (default 3) longest-failing or most-recently-regressed tests | The actionable detail |
| New failures vs. last run | Tests that passed in the prior run and failed in this one | The "what changed" answer the exec wants |
| Severity / category breakdown | Per Allure's severity and categories taxonomy when available; otherwise omitted | Risk-weighted reading of the same numbers |
If the input lacks a metric (e.g., JUnit XML has no severity), the skill emits "n/a" rather than fabricating. Citation discipline matters most here - every number cited in the narrative is a number that appears in the input data.
The skill emits one of four narrative shapes; the shape is an explicit parameter (defaults to status-update):
status-update - Slack-ready, ≤3 lines: a pass/fail lead plus deltas and ownership (the Worked example below).release-notes - PR / changelog bullets: results, new failures vs the prior release, coverage, performance, known-issue waivers.exec-summary - one paragraph plus a 4-bullet outlook for the QBR / weekly leadership update.cross-run-trend - narrative over a window of runs (last N runs / days); requires ≥5 runs.The full fill-in template for each shape is in references/output-shapes.md.
A nightly regression, status-update shape, from two JUnit XML files (this run + the prior run):
junit/results-2026-05-09.xml: 1,247 passed, 18 failed, 3 skipped -> pass rate 1247 / (1247 + 18) = 98.6%.junit/results-2026-05-08.xml: pass rate 98.9% -> delta -0.3pp.cart.checkout.spec (timeout), auth.sso.spec (assertion), payments.refund.spec (timeout).The banner emoji maps pass rate to :white_check_mark: ≥99% / :warning: 95 - 98.99% / :x: <95%, so 98.6% is :warning::
**:warning: 2026-05-09 nightly regression - 1,247 pass, 18 fail, 3 skipped.**
Pass rate 98.6% (-0.3pp vs Thu). Top regressions: `cart.checkout.spec` (timeout), `auth.sso.spec` (assertion), `payments.refund.spec` (timeout). Run: <build-url>.
Duration 1h 12m; investigation owners: @cart, @auth, @payments.Every number traces back to a source before the draft is emitted:
### Audit (sources)
| Claim | Source |
|---|---|
| 1,247 / 18 / 3 | `junit/results-2026-05-09.xml` counts |
| 98.6% pass rate | computed passed / (passed + failed) |
| -0.3pp vs Thu | `junit/results-2026-05-08.xml` (98.9%) |
| Duration 1h 12m | earliest testsuite start to latest stop, same build |Before emitting, walk each numeric claim in the draft and confirm it appears in the input data - the Worked example's audit table is the concrete form (suppressible via --no-audit once the numbers are trusted). If any claim cannot be sourced (e.g., the SLO baseline isn't in the input), flag [unsourced - supply baseline] rather than inventing it.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Inventing a trend ("regressions are up") with no prior-run data | Manager pastes the summary; exec asks "vs what?"; manager has no answer. | The Verify-against-source audit requires the prior-run source for any delta claim. |
| Citing severity when the input is JUnit XML (no severity field) | JUnit's testcase / failure schema does not carry severity. Severity from JUnit alone is fabrication. | Allure-only field; emit "n/a" for JUnit-only inputs. |
| Using pass-rate ≥99% as the default green threshold for a smoke suite | Smoke suites are tiny; one flake takes pass rate to ≤95%. | Threshold is configurable per suite kind (smoke / regression / e2e). |
| Picking top-3 failures by name only | Reader cannot tell why they matter. | Ranking is by failure-impact: longest-failing, most-recently-regressed, severity (when available). |
Producing a release-notes shape with no link to the build / report | Reader cannot drill into the data; the narrative becomes load-bearing without source. | Build URL is required in the input; the skill refuses to emit a release-notes shape without it. |
Drafting a cross-run-trend over 2 runs | Two data points are not a trend. | The cross-run-trend shape requires ≥5 runs (see references/output-shapes.md); otherwise emit INSUFFICIENT_RUNS: 2 runs supplied, 5+ required for a trend. |
cart.checkout.spec verbatim; if the team's exec audience prefers human-readable suite names, an upstream alias map is required (out of scope for this skill).release-notes shape omits the coverage bullet rather than fabricating numbers.coverage-diff-reporter.junit-xml-analysis, allure-reports.*-result.json, *-container.json), status taxonomy (passed / failed / broken / skipped / unknown), severity, categories: https://allurereport.org/docs/status-update, release-notes, exec-summary, cross-run-trend).junit-xml-analysis, allure-reports, coverage-diff-reporter - the per-tool parsers this skill consumes.