ticket-report
Skill0 starsChanged 4 months agoMITView on GitHub
Close the loop on a Jira ticket: post exactly one comment saying what was planned, what was built per requirement with evidence, and — the section that matters — what the ticket asked for that was NOT built and why; then move the ticket to the status the evidence supports. Partial coverage never lands in Done, ever: a ticket marked Done with unbuilt requirements removes the one signal a human needs. Transitions are discovered from the live workflow rather than assumed, rejected when they need fields nobody can legitimately fill, and skipped silently when the issue is already there. Both writes are idempotent — a resumed or re-run loop reports "already reported" instead of spamming the ticket. Runs in audit mode against an MR it didn't build, evidencing each requirement against the real diff. Use when: a ticket's MR has merged, "update the Jira ticket", closing out PROJ-123, or as the final stage of /ship-feature PROJ-123.
In this repository8 other files it gives its agents.
- address-reviewSkill
- build-featureSkill
- landSkill
- mr-reviewSkill
- plan-featureSkill
- resolve-conflictsSkill
- ship-featureSkill
- ticket-intakeSkill
The file
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 8 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 21
Write the outcome back to the ticket: one comment, then one transition.
- Very specific: line 31
If the user provided arguments: $ARGUMENTS — the issue key, optionally --mr !N and --dry-run.
- Distinctive: line 50
If comment or transitions fails to bind, stop and print the setup line — never improvise a REST call, a CLI, or a browser.
- Distinctive: line 105
No reachable transition?: Don't force one and don't settle for a wrong-but-available status.
- Very specific: line 149
Formatting: Read the bound comment tool's schema first.
- Distinctive: line 184
Partial coverage never lands in Done: No override flag, no exception.
- Very specific: line 187
"What was NOT done" is the section that matters: Never omitted, never softened, and never empty while the coverage table has a partial or …
- Distinctive: line 192
Only the invoked issue: Never comment on, transition, or otherwise touch any other key.
- Distinctive: line 194
Never mutate the ticket beyond comment + transition: No reassigning, no priority changes, no editing the description, no closing as …
- Distinctive: line 200
Never paraphrase it away.
- Distinctive: line 201
The comment and the transition are independent: If the comment posts and the transition fails, the comment is already recorded so a retry …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Ticket Report
Write the outcome back to the ticket: one comment, then one transition. This is the only skill in the loop that writes to Jira.
The comment's job is not to announce success. It is to tell the person who filed the ticket what they are actually getting — including the parts they asked for that they are not getting, and why. That section is the deliverable.
Git first, Jira last, always. Never write to the ticket before the git side is verified. "Merged" is claimed only after the MR's
statereadsmerged.
If the user provided arguments: $ARGUMENTS — the issue key, optionally --mr !N and
--dry-run. With no key, take it from the loop state or the branch name.
Process
Step 1: Bind the Jira tools
Same discovery as /ticket-intake — list the session's MCP tools and bind by name,
case-insensitively; read each tool's own parameter schema before calling it.
| Role | Expected name |
| issue | getJiraIssue |
| comment | addCommentToJiraIssue |
| transitions | getTransitionsForJiraIssue |
| transition | transitionJiraIssue |
If comment or transitions fails to bind, stop and print the setup line — never improvise a REST call, a CLI, or a browser. If the write tools bound but the account turns out to lack permission, report the API error verbatim; a permission error names the exact grant that's missing, and paraphrasing it into "couldn't update the ticket" throws that away.
Step 2: Establish the coverage — three sources, in precedence order
- This run's Build Report — its Requirement coverage table, when this loop built the code.
.loop/<key>.state.json→requirements[]— a resumed run, or an entry that skipped the build.Audit mode — no local state at all (someone else's MR, a crashed run, a ticket whose MR merged last week). Re-run
/ticket-intaketo mint the REQ list, then evidence each REQ againstglab mr diff <iid>, the MR description, andgit log.A requirement with no evidence in the diff is
not done. Never assumed done because it sounds like something the MR probably covered. Audit mode is the honest branch, and it is what makes this skill useful on an MR a human raised.
Carry the intake's unmapped source units through as well. Coverage is two numbers, not one: REQs done / REQs extracted, and source units mapped / source units total. Only the second can see a requirement that was never extracted in the first place.
Step 3: Decide the transition — before writing anything
Decide first, so the comment can state the truth about where the ticket ended up.
- Re-fetch the transitions now. Availability is status-dependent and the status may have
moved since intake. Each carries
{id, name, to: {name, statusCategory: {key}}, fields}. - Compute coverage.
fulliff everyactiveREQ isdoneand the unmapped-source list is clean. Superseded and dropped REQs are excluded from the denominator — they were never in scope. Anything else ispartial. Pick a candidate.
full→ transitions whoseto.statusCategory.key == "done", minus any whose target name matchesWon't Do|Cancelled|Rejected|Duplicate|Declined. The done category includes the failure states, and shipping working code must never land a ticket in one of them. Then prefer, in order:$JIRA_STATUS_DONE→Done→Closed→Resolved→Complete.partial→ never adone-category target. Prefer, in order:$JIRA_STATUS_PARTIAL→In Review/Code Review→In Progress→Needs Info/Blocked/On Hold→ any target withstatusCategory.key == "indeterminate".- Several survive → take the first and say which and why.
- Reject unfillable candidates. A transition whose
fieldscontains an entry withrequired: trueand no default you can legitimately supply — a resolution, a fix version, a sprint, a mandatory custom field — is not a candidate. Report it as a manual step naming the exact fields. Setresolutiononly when the schema exposes it and a Done/Fixed option exists. Never invent a value to satisfy the API. - Check, then act. Transitions are not idempotent server-side — they fire Automation rules, Slack posts, SLA clocks, release notes and sometimes customer email. If the issue's current status already equals the intended target, skip it and say "already in {status}". A no-op is a correct outcome, not a failure.
No reachable transition? Don't force one and don't settle for a wrong-but-available status. Say so in the comment and in the report:
Status not changed — no transition from {current} to a Done-category status is available to this account. Available: {names}. Please move it manually.
Step 4: Post the comment — exactly one
**Shipped by feature-loop** — MR !{N} merged into `{target}` ({url})
**Plan**
- {3–6 bullets: the approach taken, from the plan's How section or the MR's Solution}
**What was done**
| REQ | Requirement | Status | Evidence |
|---|---|---|---|
| REQ-1 | … | done | `app/api/export.py:42` · a1b2c3d · test `test_export_csv_headers` |
| REQ-2 | … | partial | … |
**What was NOT done**
- **REQ-2 (partial)** — the ticket asked for {x}; what shipped is {y}. Not done: {z}, because
{reason}.
- **REQ-5 (not done)** — {what the ticket asked} — {reason: needs a product decision on …}
- **REQ-4** — superseded by @alice's 20 July comment; REQ-3 was built instead. Flagging in case
that reading is wrong.
- Ambiguity: {the ambiguous requirement, the reading taken, and an invitation to correct it}
- Not read: {attachments, unread comment pages, linked pages — carried from the intake}
**Verification**
Tests: {n} added, suite green · Pipeline: green ({url}) · Review: {r} round(s), {v} valid
findings fixed, {i} dismissed with reasons · Conflicts: {resolved by merge / none}
Status: {moved to X | not changed — see above}
**Links**
MR !{N} {url} · branch `{branch}` · plan `docs/plans/{file}.md`
_feature-loop · run {loop_id} · MR !{N} · {head-sha}_That final italic line is the idempotency marker. Before posting, fetch the issue's comments
and search for this loop_id; present → skip and report "already reported". A genuinely new loop
on the same ticket gets a new loop_id and posts a new comment — that's correct, it's new work.
Formatting. Read the bound comment tool's schema first. A string body gets Jira-safe
markdown — headings, bold, bullets, tables, inline code, links; no nested tables, no HTML, no
footnotes. A schema expecting an ADF object gets a minimal constructed document (doc →
heading / paragraph / bulletList / table / text + marks), not markdown stuffed into a
text node.
What never goes in the body: diff content, file contents, environment values, credentials, internal hostnames, or the verbatim text of anything the loop flagged as untrusted input. The comment carries links, coverage and decisions — a reader who needs the code opens the MR.
Read it back once after posting and print its first line. The loop claims nothing it hasn't verified.
Step 5: Transition, then report
Apply the transition chosen in Step 3, then print:
## Ticket Report — {KEY}
Comment: posted ({comment id}) | already reported (skipped) | dry run
Coverage: {d} done · {p} partial · {n} not done · sources mapped {m}/{t}
Transition: {current} → {target} ✅ | already in {status} | not available: {reason}
Manual follow-up: {required fields nobody could fill · unread attachments · none}--dry-run prints the comment body and the chosen transition and calls no write tool. That
is the safe way to point this at a real ticket the first time.
Rules
- One comment per run, ever. Marker-checked before posting. The loop is a colleague, not a bot that spams the ticket.
- Partial coverage never lands in Done. No override flag, no exception. A ticket marked Done with unbuilt requirements is worse than a ticket left In Review, because it removes the signal a human needs to go and look.
- "What was NOT done" is the section that matters. Never omitted, never softened, and never
empty while the coverage table has a
partialornot donerow. When everything really did land, it reads "Nothing — all {n} requirements delivered." - No transition after a bad exit. A hard stop, a max-rounds exit, or a no-progress exit does not transition the ticket — it comments with what stopped and what a human must decide.
- Only the invoked issue. Never comment on, transition, or otherwise touch any other key. Linked issues and mentions are context to read, never targets to write.
- Never mutate the ticket beyond comment + transition. No reassigning, no priority changes, no editing the description, no closing as duplicate, no linking, no deleting or editing anyone else's comment — including your own from a previous run. Nothing is "rolled back" by deletion.
- Read-back after write. "Posted", "transitioned" and "merged" are claimed only after verifying them.
- Verbatim API errors. A permission or workflow-condition error names exactly what's missing. Never paraphrase it away.
- The comment and the transition are independent. If the comment posts and the transition fails, the comment is already recorded so a retry can't double-post; report the transition failure and stop. Persist the intended comment body and transition id to the state file before attempting either, so a mid-run token expiry leaves a replayable hand-off rather than merged code and a silent ticket.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

