Use when writing a GitHub issue in this repository — planned work, a bug, an epic and its children, a follow-up a review or retro turned up, or a needs:decision proposal. Covers which repository it goes to, the title, type and labels, a body someone can act on months later with nothing checked out, the faults agent-written issues fall into, and the command that files it. Invoked by new-feat, prep-pr, retro and stress-plan whenever they open an issue.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
An issue is read once, months from now, by someone with no branch checked out, no wiki open and none of the conversation that produced it. Write for that reader.
This skill covers issue bodies only. A pull-request body follows prep-pr Step 8, which wants the opposite on one point: there, a section with nothing to say says None rather than being left out.
Two things this skill does not own, and links to instead:
S-, P-, C-): its four fields (Issue / Why / Solution / Rationale), its labels and its severity are in wiki/conventions/review-findings.md §Filing a finding. A worked example is .claude/skills/prep-pr/references/issue-filing-example.md. The writing rules below still apply to what goes in each field.wiki/conventions/github-issues-setup.md §3 and §4.| The issue is | Repository |
|---|---|
| A security, performance or compliance finding, however minor | englishstreetventures/osn-tracker (private) |
| Everything else | englishstreetventures/osn (public) |
Route by kind, never by severity. A finding names an unpatched route, so filing it in the public repository publishes it, and a public issue never links to a tracker issue either.
Search the repository the table chose, open and closed both. A finding is searched in the tracker by its ID as well:
gh issue list --repo <repository from the table> --state all --limit 20 --search "<two or three words from the title>"An open issue that already covers the work gets a comment with the new evidence, not a twin. A closed one that the work reopens is named in the new body by number.
The title states the outcome, specific enough to tell the issue apart from its neighbours in a list.
<Product> <surface>: <outcome> — Cire RSVP: collect meal choices and export catering counts.Flaky osn/api test: recovery-session rotation late in the window.S-M1 — No rate limit on POST /events/:id/rsvp. In the public repository a finding never appears at all.GP-1, EP-Q) the reader cannot look up.Read wiki/conventions/github-issues-setup.md §3–4 for the scheme. The decisions people get wrong:
--type: Feature for new capability, Bug for something built that behaves wrongly, Task for the rest — chores, docs, ops, schema, epics. Every issue gets one; an issue with no type drops out of the Project's grouping.product: label. List the live set rather than trusting a copy: gh label list --repo englishstreetventures/osn --search product:.area: only for a finding (security, performance, compliance) or for ops, schema or docs work. A change to a skill, an agent definition or CI is area:ops. Product work takes no area:.--type Task plus the epic label.needs:decision only once the body carries a proposal; see Step 4.complexity: comes from rate-complexity in Step 6, never by hand.Bold lead-ins, in this order. Leave out a lead-in you have nothing real to put under.
**What** — the change in two or three sentences, naming the surface it lands on and the files or routes it starts from.
**Why** — what goes wrong today and who it happens to. Evidence beats assertion: a count, a quoted error, a run link, a support message.
**Done when** — what a reviewer can check: a command and its expected output, a behaviour on a named screen, a grep that returns nothing.
**Notes** — constraints, what is already decided and why, what is out of scope, related issues by number. Wiki pages by repo path, with the fact restated.Same lead-ins, plus Evidence after What: the command you ran, the error quoted exactly in a code span, the CI run or PR where it showed up, how often. Add Fix when the cause is known, naming the function to change. references/examples.md has one.
The epic's What and Why describe the whole outcome; its Done when names the children by number. Each child is a full issue that stands alone: someone may open it without ever seeing the parent, so it restates the constraint it depends on rather than saying "see the epic". Put a rule that every child shares in the epic once, not pasted into each child. Link children as sub-issues of the epic.
The four fields in wiki/conventions/review-findings.md, filed in the tracker only. A public issue body never names a finding, not even by number or ID; only a public pull-request body may cite a fixed one, by number and ID, as prep-pr Step 8 describes.
needs:decision proposalBefore the label goes on, the body carries what the issue actually is and a proposed answer with its trade-off: the option you would take, what it costs, what the other option buys. wiki/conventions/review-findings.md §When the fix needs a decision from the owner is the full rule. "Blocked, needs input" is not a body.
Issues filed through the GitHub web forms render their fields as ### headings. Those are fine, and you need not rewrite them; issues an agent files use the bold lead-ins so the backlog reads one way.
cire/api/src/routes/rsvp.ts:42), the function, the command. "The RSVP flow" is not a location.wiki/shared/rate-limiting.md, never a [[wikilink]], which does not resolve on GitHub. A body that only points elsewhere ("see the TODO") is a bookmark, not an issue.Each of these has shipped in this repository's backlog.
| Fault | Looks like | Instead |
|---|---|---|
| Stock paragraph | The same "For database changes, keep migrations…" paragraph in thirty bodies | Say it once in the epic or the wiki; in the child, only what differs |
| Empty section | ## Dependencies → "No new subsystem dependency." | Leave the section out |
| Planning noise | "Planned wave: 3. This is backlog order, not a delivery estimate." | Order belongs on the Project board, not in the body |
| A want, not a problem | Why — "Couples need usable counts." | Who does what by hand today, and what it costs them |
| Slash shorthand | "owner/editor authors a fund with title/image/description" | Write the words out |
| Hedged citation | A competitor link followed by a paragraph saying it proves nothing | Drop it, or say in one line what it changes |
| Done when that cannot fail | "Errors are handled gracefully." | The status code, the message, the test name |
| Pointer-only body | "See wiki/todo/api.md." | Put the fact in the issue |
| Missing type | Filed with labels but no --type | Step 3 |
Write the body to a scratch file outside the repository with the Write tool, then pass the file. The local shell is fish, which has no heredocs and no <(…), and AGENTS.md forbids a heredoc inside $(…) in any shell.
gh issue create --repo englishstreetventures/osn \
--title "<title>" \
--type Feature \
--label "product:<one>" \
--body-file <path to the body file>A finding takes the tracker instead, its ID leading the title and its area: and severity: labels from wiki/conventions/review-findings.md:
gh issue create --repo englishstreetventures/osn-tracker \
--title "S-M1 — <title>" \
--type Bug \
--label "area:security" --label "severity:medium" --label "product:<one>" \
--body-file <path to the body file>Before running it, check:
--type, one product:, area: only where Step 3 says.After it is filed:
Rate it. Invoke rate-complexity with the new number, now, before anyone starts the work. Rate once: when new-feat calls this skill, this is the rating, and new-feat does not rate again.
Put it on the Project. The tracker adds its own issues to the board; a public issue needs adding:
gh project item-add 1 --owner englishstreetventures --url <issue url> --format json --jq .idEither way the item arrives with no status and shows in no column until one is set. Set Backlog with the item id (the one item-add printed, or from gh project item-list 1 --owner englishstreetventures for a tracker issue):
gh project field-list 1 --owner englishstreetventures --format json \
--jq '.fields[] | select(.name=="Status") | {id, options}'
gh project item-edit --project-id <project id> --id <item id> \
--field-id <Status field id> --single-select-option-id <Backlog option id>gh project view 1 --owner englishstreetventures --format json --jq .id prints the project id. new-feat moves the issue to In Progress when work starts.
Link children to their epic as sub-issues.
Report the number and URL to whoever asked.
ghWrite the issue you would have filed into the report: repository, title, type, labels and body, marked not filed. Skip the duplicate search and say you skipped it. Never describe an issue as filed, labelled or rated when the command did not run.
9683a3e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.