Ticket Writing
Write tickets someone else can pick up in five minutes. Bugs, features, and security findings.
Install
$ zana add ticket-writingThe checker is stdlib-only Python 3.9+, so there is nothing to install: `python3 scripts/ticket_check.py ticket.md`. Use `--strict` to gate in CI, and `--type bug|feature|security` when a draft is too unstructured to detect. Edit `references/house-rules.md` before using this anywhere but Amini. It is the only file that carries organisation-specific detail. Internal note: `references/house-rules.md` section 2 lists internal team keys and product names. Scrub or genericise it before this repo is made public.
How it runs
A ticket someone who was not in the conversation can start in five minutes.
- 01
Shape
Bug, feature, or security finding. They fail differently
- 02
Route
File under the team that owns it, not the one that noticed
- 03
Title
Behaviour plus condition. The condition is the lead
- 04
Fill
Every field, or an explicit reason it does not apply
- 05
ticket_check
Missing fields, vague verbs, leaked credentials
- 06
Ready
Read it once as someone who was not there
Marked steps are enforced by a script.
Sample work
What this skill actually produced. Not mockups.
Worked example: a real bug ticket, before and after
examples/deep-research-timeoutA Bridge bug as it was actually filed, and the same underlying report rewritten under this skill. Nothing was invented in the rewrite that a reporter would not have had in front of them.
## Deep research tool not working across all Bridge environments (prod, staging, dev) The deep research tool is not working properly in any Bridge environment. Prod, staging, and dev are all affected. Evidence of the problem - Timeouts when generating templates. - Bridge fails to return a response when it gets a difficult question in the research section, because that triggers deep research. Environments affected - Production - Staging - Dev
## [Bridge] Deep research requests time out with no response in all environments ## Summary Deep research requests never return. Affects prod, staging, and dev, which points to a shared upstream dependency rather than per-env config. Blocks template generation and any research question that escalates to deep research. ## Environment - Env: prod, staging, and dev - Service and repo: bridge-backend (aminitech/bridge-backend) - Image sha: prod `sha-4c1a2b`, staging `sha-9f0e77`, dev `main @ a3f9c21` - Deployed via the container platform; upstream deep-research-svc in us-west-2 - Browser and OS: Chrome 138 on macOS 15.4, all requests via the Bridge web UI - Account used: qa-bridge-03 ## Steps to reproduce
02-rewritten.md =============== type bug title [Bridge] Deep research requests time out with no response in all environ nothing flagged score 100/100, ready to pick up
01-as-filed.md
==============
type bug
title Deep research tool not working across all Bridge environments (prod, sta
structure
- -6 no recognisable ticket shape no headings named the fields; checked as a bug, pass --type to override
missing fields
- -12 impact who is blocked and how many; this is what sets priority
- -12 steps to reproduce without them the first hour goes on guessing
- -12 expected separates a bug from a design disagreement
- -12 actual separates a bug from a design disagreement
- -6 summary one or two lines on what breaks or what is missing, for whom, how bad
- -6 environment prod, staging, dev, or on-prem, and which service
- -6 build identity the sha or tag proves which build is actually running
- -6 reproducibility 10 out of 10 and 1 out of 10 point at different root causes18 files. Read them here, or pull the folder without the CLI.
$ npx degit aminitech/amini-zana/catalog/skills/ticket-writing ~/.claude/skills/ticket-writingNo clone, no history. Just this folder.
$ git clone --depth 1 --filter=blob:none --sparse https://github.com/aminitech/amini-zana.git && cd amini-zana && git sparse-checkout set catalog/skills/ticket-writing$ curl -sL https://raw.githubusercontent.com/aminitech/amini-zana/main/catalog/skills/ticket-writing/SKILL.md--- name: Ticket Writing description: >- Write tickets someone else can actually pick up. Use whenever a bug, feature, chore, or security finding is being filed, rewritten, triaged, or groomed, in Linear, Jira, or GitHub Issues. Also fires on "file a ticket for this", "turn this thread into an issue", "write acceptance criteria", "is this ticket ready?", "this ticket is unworkable". Ships templates for bugs, features, and security findings, plus a checker that scores a draft and names the missing fields. license: MIT metadata: author: Amini version: "1.0.0" --- # Ticket Writing A ticket is a handoff of context. Whoever picks it up, maybe you in three weeks, maybe a new hire, maybe an on-call engineer at 02:00, should be able to reproduce it, verify it, and close it without reopening a chat thread that has since scrolled away. The bar is one question: **could someone who was not in the conversation start work in under five minutes?** Most tickets fail that bar for boring reasons. The reporter had the failure in front of them and wrote down the part they found surprising, which is rarely the part that locates the cause. This skill fixes the shape of the ticket, not the writing style. ## Read these as needed - `references/house-rules.md` is the only file you have to edit to retarget this. Tracker, team routing, house style, build identity, confidentiality. **Read it first**, because team routing and style rules change what you write. - `references/bug-tickets.md`, `references/feature-tickets.md`, and `references/security-findings.md` cover the three shapes in depth. - `references/severity-and-priority.md` is the two-axis rule that stops the arguing. - `references/ready-and-done.md` has the ticket-level Definition of Ready and Definition of Done. - `references/anti-patterns.md` is the list of things to stop doing, with what to do instead. - `templates/` has the three templates as paste-ready markdown. - `examples/deep-research-timeout/` is a real ticket as filed (30/100), the same ticket rewritten (100/100), both checker reports, and a gap-by-gap breakdown of what each missing field cost. ## Pick the shape first Three shapes, and they fail differently. | Shape | Fails because | Template | | --- | --- | --- | | Bug | Under-specified. Nobody can reproduce it. | `templates/bug.md` | | Feature | Specified but untestable. "Done" gets decided by argument in review. | `templates/feature.md` | | Security finding | Neither of the above. Needs CVSS, a sanitized proof of concept, and closure with proof. | `templates/security-finding.md` | A chore, a dependency bump, or a config tweak is a small feature. Use the feature template and a checklist instead of Gherkin. ## How to write one 1. **Pick the shape and the owning team.** File under the team that owns the work, not the team that noticed. Cross-team work goes to the owner with blocked-by and blocks relations for the rest. 2. **Write the title as behaviour plus condition.** `[Team or Component] <what breaks> <under what condition>`. The condition is the part people leave out and the part that locates the bug. 3. **Fill the template.** Every field, or an explicit statement of why it does not apply. "Not reliably reproducible, 1 in 20, trace from the one capture below" is a filled field. A blank one is a question you have deferred onto someone else. 4. **State impact separately from severity.** Impact sets priority. Severity is technical damage. See `references/severity-and-priority.md`. 5. **Run the checker.** Fix what it flags, then read it once as someone who was not there. 6. **Link the thread, do not paste it.** Extract the facts first. ## The rules that carry most of the weight **One ticket per thing.** If the "and" in your title joins two unrelated failures, split it. Bundled tickets get half-fixed and closed. One cause with several symptoms is still one ticket, and listing all the symptoms is what points at the shared cause. **Quote the error.** Verbatim, in a code fence, with the status code. "It failed" is not searchable. A gateway 504, an app 500, and a proxy 404 point at completely different layers. **Give the build identity.** Commit, tag, or image sha. Container names are opaque hashes and prove nothing. This is also how you close a ticket honestly: the fix is not done when it is merged, it is done when the sha carrying it is the one running. **Put a timezone on every timestamp.** Distributed team, UTC logs, dashboards rendering local time. Without a timezone, three readers search three different log windows. **Describe the problem, not the fix.** "Add a retry button" has already made the design decision and hidden the real failure. Sometimes the answer is a retry button. Sometimes the answer is that the request should not have failed. **Reproducibility rate is data.** 10 out of 10 and 1 out of 10 point at different root causes. If you cannot reproduce it, say so explicitly. That is diagnostic information, not a gap. ## Check the draft ```bash python3 scripts/ticket_check.py ticket.md python3 scripts/ticket_check.py ticket.md --type feature # skip detection python3 scripts/ticket_check.py tickets/*.md --strict # non-zero exit below 85 cat ticket.md | python3 scripts/ticket_check.py - ``` Stdlib only, no install. Paths are relative to this skill's directory. It detects the shape from the headings, checks the fields that shape needs, and reports missing fields, vague language, unfilled template placeholders, timestamps with no timezone, severity and priority stated together, procedural Gherkin, happy-path-only acceptance criteria, and anything that looks like a live credential pasted into a proof of concept. Then a score out of 100. Rough calibration: a structured but under-specified ticket, which is the most common case, lands around 30. A copied template with the headings still empty is clamped to 35 and labelled as unfilled, so it cannot read as ready. Above 85 is pick-up-able. The score is a smoke alarm, not a grade. It cannot tell whether the steps actually reproduce, whether the acceptance criteria describe the right behaviour, or whether the impact claim is true. Passing it is the floor. **One hard failure.** Anything matching a live credential costs 25 points on its own and should be fixed before the ticket is saved, not before it is submitted. Redact it and rotate the key. ## Reviewing someone else's ticket Run the checker, then check the one thing it cannot: does the ticket describe a problem, or a solution someone has already picked? Send it back against `references/ready-and-done.md` if it fails the Definition of Ready. That is not obstruction. It is cheaper than finding the gap halfway through the implementation, and much cheaper than finding it in review. Be specific about what is missing and why it costs something. "Needs more detail" is the same anti-pattern the ticket is guilty of. ## What not to do Do not invent the missing fields. If the reporter did not give you a request ID, a timestamp, or a reproduction rate, the ticket says so and asks. A confidently wrong environment sends someone to the wrong logs, which is worse than an empty field. Do not paste real credentials, tokens, session cookies, or user data into a ticket, including into a security proof of concept, on the reasoning that the tracker is internal. Exports, screenshots, and integrations all leak. Do not restate release-stage gates in a ticket. Ticket-level Ready and Done are about one ticket. Whether the whole product can advance to beta or GA is a different document, owned by the engineering owner. Link it.
About
A ticket is a handoff of context. Whoever picks it up, maybe you in three weeks, maybe an on-call engineer at 02:00, should be able to reproduce it, verify it, and close it without reopening a chat thread that has since scrolled away. This skill fixes the shape of the ticket rather than its prose. It covers the three shapes that fail in different ways. Bugs are under-specified, so the first hour goes on guessing: the template forces steps, expected against actual, verbatim error text, reproducibility rate, build identity, and last known good. Features are specified but untestable, so "done" gets decided by argument in review: acceptance criteria are written as Gherkin scenarios before the work starts, with rules for keeping them declarative and a note on when a checklist is the honest answer instead. Security findings are neither, and need CVSS and CWE, a sanitized proof of concept, and closure with proof rather than "should be fixed". Two rules carry most of the weight. Severity and priority are separate axes, technical damage against order of work, which is why a cosmetic typo on a public page can be S4 and Urgent while a broken internal tool is S2 and Low. And build identity, the commit or image sha, because merged is not deployed and a container name proves nothing. Ships `scripts/ticket_check.py`, a stdlib-only checker that detects the ticket shape from its headings, scores it out of 100, and names what is missing: absent fields, vague language, unfilled template scaffolding, timestamps with no timezone, procedural Gherkin, happy-path-only criteria, and anything matching a live credential pasted into a proof of concept. A ticket copied out of a chat thread scores around 30. The worked example rewrites a real one from 30 to 100 and shows what each missing field cost. Retarget it by editing one file, `references/house-rules.md`: tracker, team routing, house style, build identity, confidentiality. The method works the same in Linear, Jira, or GitHub Issues.
Amini 路 Manila, Philippines