diff --git a/.opencode/skills/create-issue/SKILL.md b/.opencode/skills/create-issue/SKILL.md new file mode 100644 index 0000000000..90a0fc3b75 --- /dev/null +++ b/.opencode/skills/create-issue/SKILL.md @@ -0,0 +1,27 @@ +--- +name: create-issue +description: Create or update GitHub issues (from PR, from draft body, retitle existing). Routes to the canonical flow in `mem:workflow/creating-issues`. +--- + +# Skill: create-issue + +Entry point for all GitHub issue work. All rules (title derivation, metadata, +body templates, Issue Type IDs), all flows, and all `gh` / GraphQL commands +live in `mem:workflow/creating-issues` (file: +`.serena/memories/workflow/creating-issues.md`). This skill routes to the +right flow. + +## When to Use + +- **Create from PR** — PR exists; the issue is the changelog/release unit, + the PR is the implementation. Issue = WHAT, PR = HOW. + → memory section **Creating Issues from PRs** +- **Create from draft body** — Taiga story, user report, discussion; no PR + yet. + → memory section **Creating Issues from Draft Body** +- **Retitle existing issue** — current title is vague, prefixed, or stale. + → memory section **Retitling an Existing Issue** + +Everything else (title derivation, metadata policy, body templates, Issue +Type IDs, create/verify/cleanup commands) lives in the memory — go to the +matching section there. diff --git a/.opencode/skills/gh-issue-from-pr/SKILL.md b/.opencode/skills/gh-issue-from-pr/SKILL.md deleted file mode 100644 index bb7fe10cf9..0000000000 --- a/.opencode/skills/gh-issue-from-pr/SKILL.md +++ /dev/null @@ -1,227 +0,0 @@ ---- -name: gh-issue-from-pr -description: Create a user-facing GitHub issue from a PR, separating the WHAT from the HOW, with correct milestone, project, labels, and issue type. ---- - -# Skill: gh-issue-from-pr - -Create a GitHub issue that captures the **WHAT** (user-facing feature or -bug) from an existing PR that describes the **HOW** (implementation). -Used when the project board needs an issue as the primary changelog/release unit. - -## When to Use - -- Create a tracking issue from a PR for changelog purposes -- Extract the user-facing problem/feature from a PR's implementation details -- Assign milestone, project, labels, and issue type to a new issue derived from a PR - -## Prerequisites - -- `gh` CLI authenticated (`gh auth status`) -- Permission to create issues and edit PRs in the target repository - -## Workflow - -### 1. Understand the PR - -```bash -gh pr view --repo penpot/penpot \ - --json title,body,author,labels,baseRefName,mergedAt,state,milestone -``` - -Identify: - -- **WHAT** — user-facing problem or feature. Goes into the issue. - Describe symptoms and impact, not internal mechanisms. -- **HOW** — implementation details. These belong in the PR, not the issue. - -### 2. Determine metadata - -| Field | Source | Rule | -|-------|--------|------| -| **Title** | PR title | Rewrite from user perspective. Strip leading emoji prefixes (`:bug:`, `:sparkles:`, `:tada:`). Focus on observable behavior. Use imperative mood. Use the `issue-title` skill to generate this. | -| **Labels** | PR labels | Copy `community contribution` if present. Skip `bug` and `enhancement` (redundant with Issue Type). Skip workflow labels (`backport candidate`, `team-qa`). | -| **Milestone** | PR milestone | **Always copy what's on the PR.** Fetch with: `gh pr view --json milestone --jq '.milestone.title'` If the PR has no milestone, create the issue without one. | -| **Project** | Always `Main` | Penpot uses the `Main` project (number 8) for all issues. | -| **Body** | PR's user-facing section | Extract steps to reproduce or feature description. Omit internal details. Use templates below. | -| **Issue Type** | PR labels / title | Map: `bug` label or `:bug:` title → `Bug`. `enhancement` label or `:sparkles:` title → `Enhancement`. Feature/epic → `Feature`. Default → `Task`. | - -### 3. Write the issue body - -**Bug template:** - -```markdown -### Description - - - -### Steps to reproduce - -1. -2. - -### Expected behavior - - - -### Affected versions - - -``` - -**Enhancement template:** - -```markdown -### Description - - - -### Use case - - - -### Affected versions - - -``` - -### 4. Create the issue - -Write the body to a temp file to avoid shell quoting issues: - -```bash -cat > /tmp/issue-body.md << 'ISSUE_BODY' - -ISSUE_BODY -``` - -Create: - -```bash -gh issue create \ - --repo penpot/penpot \ - --title "" \ - --label "community contribution" \ # only if PR has this label - --milestone "<milestone>" \ - --project "Main" \ - --body-file /tmp/issue-body.md -``` - -Output: `https://github.com/penpot/penpot/issues/<NUMBER>` - -### 5. Assign to the PR author - -Assign the issue to the PR author so they're responsible for it: - -```bash -AUTHOR=$(gh pr view <PR_NUMBER> --repo penpot/penpot --json author --jq '.author.login') -gh issue edit <ISSUE_NUMBER> --repo penpot/penpot --add-assignee "$AUTHOR" -``` - -### 6. Set the Issue Type - -`gh issue create` can't set the Issue Type directly. Use GraphQL. - -Get the issue's GraphQL node ID: - -```bash -ISSUE_ID=$(gh api graphql -f query=' -query { repository(owner: "penpot", name: "penpot") { - issue(number: <ISSUE_NUMBER>) { id } -}}' --jq '.data.repository.issue.id') -``` - -Issue Type IDs for the Penpot repo: - -| Type | ID | -|------|----| -| Bug | `IT_kwDOAcyBPM4AX5Nb` | -| Enhancement | `IT_kwDOAcyBPM4B_IQN` | -| Feature | `IT_kwDOAcyBPM4AX5Nf` | -| Task | `IT_kwDOAcyBPM4AX5NY` | -| Question | `IT_kwDOAcyBPM4B_IQj` | -| Docs | `IT_kwDOAcyBPM4B_IQz` | - -Set it: - -```bash -gh api graphql -f query=' -mutation { - updateIssue(input: { - id: "'"$ISSUE_ID"'" - issueTypeId: "<TYPE_ID>" - }) { - issue { number issueType { name } } - } -}' -``` - -### 7. Verify - -```bash -gh issue view <ISSUE_NUMBER> --repo penpot/penpot \ - --json title,milestone,projectItems,labels \ - --jq '{title, milestone: .milestone.title, projects: [.projectItems[].title], labels: [.labels[].name]}' - -gh api graphql -f query=' -query { repository(owner: "penpot", name: "penpot") { - issue(number: <ISSUE_NUMBER>) { issueType { name } } -}}' --jq '.data.repository.issue.issueType.name' -``` - -### 8. Link the PR to the issue - -Append `Closes #<ISSUE_NUMBER>` to the PR body: - -```bash -gh pr view <PR_NUMBER> --repo penpot/penpot --json body --jq '.body' > /tmp/pr-body.md -printf "\n\nCloses #<ISSUE_NUMBER>\n" >> /tmp/pr-body.md -gh pr edit <PR_NUMBER> --repo penpot/penpot --body-file /tmp/pr-body.md - -# Verify -gh pr view <PR_NUMBER> --repo penpot/penpot --json body \ - --jq '.body | test("Closes #<ISSUE_NUMBER>")' -``` - -**Note:** If the PR is already merged, `Closes` won't auto-close the issue -— it only creates the "Development" sidebar link. This is the desired -behavior since the issue is a tracking artifact. - -### 9. Clean up - -```bash -rm -f /tmp/issue-body.md /tmp/pr-body.md -``` - -## Label rules - -| PR has | Issue gets | -|--------|-----------| -| `community contribution` | `community contribution` | -| `bug`, `enhancement` | *(skip — redundant with Issue Type)* | -| `backport candidate` | *(skip — workflow label)* | -| `team-qa` | *(skip — workflow label)* | - -## Issue Type mapping - -| PR label(s) / title prefix | Issue Type | -|----------------------------|-----------| -| `bug` or `:bug:` | Bug | -| `enhancement` or `:sparkles:` or `:tada:` | Enhancement | -| Feature / epic | Feature | -| Documentation | Docs | -| None of the above | Task | - -## Key Principles - -- **Issue = WHAT, PR = HOW.** Never put implementation details in the - issue body. The issue is for users, QA, and changelog readers. -- **Copy the milestone from the PR.** Don't guess based on branch names. - If the PR has no milestone, create the issue without one. -- **Set Issue Type via GraphQL** — `gh issue create` can't set it. -- **Link via PR body** — `Closes #<NUMBER>` creates the "Development" - sidebar link automatically. -- **One issue per PR** — even if a PR fixes multiple things, create a - single issue that summarizes the overall change. -- **Community attribution:** if the PR has the `community contribution` - label or the author is not a core team member, add the label to the issue. diff --git a/.opencode/skills/issue-title/SKILL.md b/.opencode/skills/issue-title/SKILL.md deleted file mode 100644 index 6cd8c14fc5..0000000000 --- a/.opencode/skills/issue-title/SKILL.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -name: issue-title -description: Derive a clear, well-formatted title for a GitHub issue from its description body, using descriptive present-tense for bugs and imperative mood for features, always including the "where" (location in the UI/module). ---- - -# Skill: issue-title - -Derive a concise, descriptive title for a GitHub issue based on its body -content. Use **descriptive present tense for bugs** (e.g. "Plugin API -crashes when setting text fills") and **imperative mood for features** (e.g. -"Add customizable dash and gap controls"). No emoji or type prefixes -(`feat:`, `bug:`, `feature:`, etc.). - -Can be used both when **creating a new issue** and when **updating an -existing one** that has a vague or outdated title. - -## When to Use - -- Creating a new issue and need a well-formatted title from the draft body -- An existing issue has a vague, outdated, or auto-generated title (e.g. - `[PENPOT FEEDBACK]: ...`, `feature: ...`) -- The current title doesn't reflect the actual content of the description -- The title is missing the "where" (which part of the UI/module is affected) - -## Prerequisites - -- `gh` CLI authenticated (`gh auth status`) - -## Workflow - -### 1. Get the issue body - -For an **existing issue**, fetch it: - -```bash -gh issue view <NUMBER> --repo penpot/penpot --json title,body -``` - -For a **new issue**, read the draft body from wherever it was provided -(Taiga link, user report, discussion, etc.). - -### 2. Read the body and derive a title - -Extract the core problem or request from the description. Distinguish between -bug reports and feature requests: - -**Bug titles (descriptive, present tense):** -Describe the symptom as it appears to the user. Format: -`[Where] [present-tense verb] when [condition]` - -- *"Plugin API crashes when setting text fills"* -- *"Canvas renders glitches when zooming quickly"* -- *"French Canada locale falls back to French (fr) translations"* -- *"Text layer content is not deleted when WebGL render is enabled"* - -Do **not** start bug titles with "Fix" or any imperative verb. The title -should state what's broken, not command a fix. - -**Feature / Enhancement titles (imperative mood):** -Command what should be built. Format: -`[Imperative verb] [what] in/on [where]` - -- *"Add customizable dash and gap length controls to dashed strokes in the sidebar"* -- *"Show user, timestamp, and hash in the workspace history panel like git commits"* -- *"Validate shape on add-object to catch malformed inputs early"* - -**Universal rules (both types):** -- **Include the "where"** — specify the UI location or module (e.g. - "in the sidebar", "in the workspace history panel", "on the stroke - options") -- **No prefixes** — strip `bug:`, `feature:`, `feat:`, `:bug:`, `:sparkles:`, - `[PENPOT FEEDBACK]`, etc. -- **No emoji** — plain text only -- **Be specific** — prefer concrete detail over generality. If the - description mentions two related problems, capture both. - -**Examples:** - -| Original / draft title | Type | New title | -|---|---|---| -| `[PENPOT FEEDBACK]: WebGL` | Bug | `Canvas renders glitches when zooming quickly — text appears distorted and nodes have background-colored rectangles` | -| `bug: flatten-nested-tokens-json uses $type instead of $value as the DTCG token/group discriminator` | Bug | `Token import fails when group-level type inheritance is used — parser misidentifies groups as tokens` | -| `feature: Dashed stroke customization` | Feature | `Add customizable dash and gap length controls to dashed strokes in the sidebar` | -| `feature: Add more detail to history of actions` | Feature | `Show user, timestamp, and hash in the workspace history panel like git commits` | - -### 3. Apply the title - -**If updating an existing issue:** - -```bash -gh issue edit <NUMBER> --repo penpot/penpot --title "<NEW TITLE>" -``` - -**If creating a new issue:** - -```bash -gh issue create --repo penpot/penpot --title "<NEW TITLE>" --body "<BODY>" -``` - -### 4. Confirm - -For updates, the command returns the issue URL. Verify by optionally fetching -again: - -```bash -gh issue view <NUMBER> --repo penpot/penpot --json title -``` - -## Key Principles - -- **Bug titles describe the symptom** — present tense, 3rd person: - "crashes", "fails", "shows", "is cut off", "does not load". Do not - start with "Fix" or "Bug:". -- **Feature titles use imperative mood** — command form: "Add", "Show", - "Use", "Validate", "Support", "Toggle". -- **Always include the "where"** — a title like "Crashes when zooming" - is too vague; "Canvas crashes when zooming quickly" is clear. -- **No prefixes, no emoji** — strip all type labels and decorative - characters from the title. -- **Derive from the body, not the current title** — the body contains - the real detail; the current title may be auto-generated or stale. -- **Two problems → cover both** — if the description has two distinct - but related issues, capture both in the title joined by "and". diff --git a/.serena/memories/workflow/creating-issues.md b/.serena/memories/workflow/creating-issues.md index 42a07f7887..46ca109c3c 100644 --- a/.serena/memories/workflow/creating-issues.md +++ b/.serena/memories/workflow/creating-issues.md @@ -155,6 +155,199 @@ query { repository(owner: "penpot", name: "penpot") { rm -f /tmp/issue-body.md ``` +## Creating Issues from PRs + +Used when the project board needs an issue as the primary changelog/release +unit and the PR describes the implementation. The issue is the **WHAT** +(user-facing), the PR is the **HOW** (implementation). + +### Fetch the PR + +```bash +gh pr view <PR_NUMBER> --repo penpot/penpot \ + --json title,body,author,labels,baseRefName,mergedAt,state,milestone +``` + +Identify: + +- **WHAT** — user-facing problem or feature. Goes into the issue. + Describe symptoms and impact, not internal mechanisms. +- **HOW** — implementation details. These belong in the PR, not the issue. + +### Determine metadata + +- **Title:** rewrite from user perspective using the title rules above. Strip + leading emoji prefixes (`:bug:`, `:sparkles:`, `:tada:`). Focus on + observable behavior. +- **Labels:** copy `community contribution` if present on the PR. +- **Milestone:** always copy what's on the PR. + + ```bash + gh pr view <PR_NUMBER> --json milestone --jq '.milestone.title' + ``` + + If the PR has no milestone, create the issue without one. +- **Project:** `Main`. +- **Body:** extract the user-facing section (steps to reproduce or feature + description). Omit internal details. Use the templates above. +- **Issue Type:** use the mapping table above (also handles `:bug:` / + `:sparkles:` / `:tada:` title prefixes). + +### Create the issue + +```bash +cat > /tmp/issue-body.md << 'ISSUE_BODY' +<body content here> +ISSUE_BODY + +gh issue create \ + --repo penpot/penpot \ + --title "<Title>" \ + --label "community contribution" \ # only if PR has this label + --milestone "<milestone>" \ + --project "Main" \ + --body-file /tmp/issue-body.md +``` + +Output: `https://github.com/penpot/penpot/issues/<NUMBER>` + +### Assign to the PR author + +```bash +AUTHOR=$(gh pr view <PR_NUMBER> --repo penpot/penpot --json author --jq '.author.login') +gh issue edit <ISSUE_NUMBER> --repo penpot/penpot --add-assignee "$AUTHOR" +``` + +### Set Issue Type and verify + +See the **Setting the Issue Type** and **Verification** sections above — the +GraphQL mutations and `gh issue view` calls are identical regardless of how +the issue was sourced. + +### Link the PR to the issue + +Append `Closes #<ISSUE_NUMBER>` to the PR body: + +```bash +gh pr view <PR_NUMBER> --repo penpot/penpot --json body --jq '.body' > /tmp/pr-body.md +printf "\n\nCloses #<ISSUE_NUMBER>\n" >> /tmp/pr-body.md +gh pr edit <PR_NUMBER> --repo penpot/penpot --body-file /tmp/pr-body.md + +# Verify +gh pr view <PR_NUMBER> --repo penpot/penpot --json body \ + --jq '.body | test("Closes #<ISSUE_NUMBER>")' +``` + +**Note:** If the PR is already merged, `Closes` won't auto-close the issue — +it only creates the "Development" sidebar link. This is the desired +behavior since the issue is a tracking artifact. + +### Clean up + +```bash +rm -f /tmp/issue-body.md /tmp/pr-body.md +``` + +### Rules for this flow + +- **One issue per PR** — even if a PR fixes multiple things, create a single + issue that summarizes the overall change. +- **Community attribution:** if the PR has the `community contribution` + label or the author is not a core team member, add the label to the issue. +- **Don't put implementation details in the issue body** — the issue is for + users, QA, and changelog readers. + +## Creating Issues from Draft Body + +Used when the user provides a draft body from elsewhere (Taiga story, user +report, discussion transcript) and there is no PR yet. + +### Get the body + +Read the draft body from wherever it was provided. If the user gives only a +vague one-liner, ask them to expand it (steps to reproduce, expected vs. +actual, use case) before proceeding. + +### Derive the title + +Apply the title rules in the **Title Derivation** section above. Distinguish +bug vs. feature from the body content: + +- Steps to reproduce + expected vs. actual → bug +- "would be nice", "add support for", "allow users to" → feature / enhancement + +### Choose a body template + +Use the bug or enhancement template from the **Issue Body Template** section +above. Fill in placeholders with the user-provided details. If the body +doesn't fit either, ask the user which template to use. + +### Determine metadata + +- **Project:** `Main` (always). +- **Milestone:** ask the user if not obvious; otherwise omit. +- **Labels:** usually none for new user-reported issues. Add + `community contribution` if the user is a non-team contributor. +- **Issue Type:** use the mapping table above (bug description → Bug; feature + request → Enhancement or Feature). + +### Create the issue + +```bash +cat > /tmp/issue-body.md << 'ISSUE_BODY' +<body content here> +ISSUE_BODY + +gh issue create \ + --repo penpot/penpot \ + --title "<Title>" \ + --label "community contribution" \ # only if applicable + --milestone "<milestone>" \ # only if provided + --project "Main" \ + --body-file /tmp/issue-body.md +``` + +### Set Issue Type and verify + +Same GraphQL mutation and `gh issue view` commands as in the +**Setting the Issue Type** and **Verification** sections above. + +### Clean up + +```bash +rm -f /tmp/issue-body.md +``` + +## Retitling an Existing Issue + +Used when an issue's current title is vague, prefixed, or no longer matches +the body (e.g. `[PENPOT FEEDBACK]: ...`, `feature: ...`). + +### Fetch the issue + +```bash +gh issue view <NUMBER> --repo penpot/penpot --json title,body +``` + +### Derive a new title + +Read the body (not the current title) and apply the title rules in the +**Title Derivation** section above. + +### Apply the new title + +```bash +gh issue edit <NUMBER> --repo penpot/penpot --title "<NEW TITLE>" +``` + +### Confirm + +```bash +gh issue view <NUMBER> --repo penpot/penpot --json title +``` + ## See Also -- Creating issues **from PRs** (separating WHAT from HOW): `mem:workflow/creating-prs` +- End-to-end orchestration entry point: the `create-issue` skill at + `.opencode/skills/create-issue/SKILL.md`. The skill is a thin entry + point; this memory is the canonical home for all issue-creation rules.