# Generate Issue  -  Shared Flow (generate)

> **TLDR**  -  Shared 12-step flow for `/multi-agent:create-jira`. Asks the issue type (Task / Bug / Story), mines the target project's existing same-type issues to learn team conventions, detects the active sprint, drafts a standards-compliant issue from a fixed standard template with auto-sizing sections, asks the user about every genuinely unknown field, renders a full preview, and creates the Jira issue only after explicit approval. Creates exactly one Jira issue per run  -  no branches, no commits, no worktrees.

Consumed by `create-jira/SKILL.md`. This ref is never invoked directly.

## Hard rules (must not regress)

- **Approval gate is absolute.** Step [9/12] (full preview + explicit approval) runs on every invocation. There is no autopilot variant of this command and no preference that skips the gate. `Cancel` means nothing was created.
- **Never invent content.** Anything the user did not supply and mining could not derive stays an open question (step [8/12]) or is omitted entirely  -  never fabricated repro steps, environments, acceptance criteria, or test pass/fail results.
- **Standard template is the baseline.** The description structure comes from the type's standard template (see [7/12]), not from a mined heading set. Mining informs summary prefix, labels, components, priority norm, sprint placement, and test-scenario style  -  never replaces the standard section skeleton.
- **Auto-sizing.** A conditional section renders only when its trigger is present. No empty placeholder headings  -  if there is nothing to put in a conditional section, it does not appear.
- **Issue content language follows `prefs.global.outputLanguage`.** Summary and description are written in the user's output language, like every other user-facing payload body (`rules.md` matrix). Code identifiers, file paths, and URLs stay verbatim. AskUserQuestion `question`/`description` also follow `outputLanguage`; `label`/`header` stay English.
- **UTF-8 verbatim POST path** (same as `$HOME/.claude/multi-agent-refs/channels/jira.md`): description body goes to a file, `jq -n --rawfile` builds the payload, `curl --data-binary @file` ships it. Never round-trip through `unicode_escape`/`latin-1`, never hand-roll a re-encoding helper. Jira wiki markup (`h3.` headings, `[text|url]` links), real newlines, no HTML entities.
- **Humanizer pass** on the description body after composition, before the preview render.
- **Read-only until approval.** Steps 1-9 perform only GET requests. The first write of any kind is the `POST /rest/api/2/issue` in step [10/12].

## Standard templates

Each type has a standard section skeleton. `A` = always present. `C` = conditional (renders only when its trigger fires).

| Type | Section | Presence | Trigger (for `C`) |
|---|---|---|---|
| **Task** | Detailed Description | A | |
| | Scope | A | |
| | Acceptance Criteria | A | |
| | Test Scenarios | A | populated from mined test-scenario style; skeleton derived from AC when the project has no prior style |
| | API Contract / Swagger | C | Swagger URL, OpenAPI/contract snippet, or explicit endpoint mention in input |
| | Design Reference | C | Figma URL given |
| | Screenshots | C | user pasted image(s) or a Figma screenshot was fetched |
| | Notes | C | real content only |
| **Bug** | Detailed Description | A | |
| | Steps to Reproduce | A | |
| | Expected Result | A | |
| | Actual Result | A | |
| | Environment | A | |
| | Screenshots / Logs | C | user pasted image(s) or log/stack-trace text |
| | Regression / Test Scenario | C | derivable from the repro; else omitted |
| | API Contract | C | API-related bug + Swagger/contract/endpoint given |
| | Design Reference | C | Figma URL given |
| | Notes | C | real content only |
| **Story** | User Story (`As a ... I want ... so that ...`) | A | |
| | Detailed Description | A | |
| | Scope | A | |
| | Acceptance Criteria | A | |
| | Test Scenarios | A | same as Task |
| | API Contract / Swagger | C | Swagger URL / contract / endpoint mention |
| | Design Reference | C | Figma URL given |
| | Screenshots | C | user pasted image(s) or Figma screenshot fetched |
| | Dependencies / Notes | C | real content only |

Section headings render in `outputLanguage` (e.g. tr: "Detaylı Açıklama", "Kapsam", "Kabul Kriterleri", "Test Senaryoları", "Adımlar", "Beklenen Sonuç", "Gerçekleşen Sonuç", "Ortam", "Ekran Görüntüleri", "API Contract", "Design Reference", "Notlar").

## Flow

### [1/12] Pick account (`_account-picker`)

```bash
ACCOUNTS=$(~/.claude/lib/account-resolver.sh --providers jira)
TOKEN=$(~/.claude/lib/credential-store.sh get "$ACCOUNT_JIRA_TOKEN_KEY")
```
- Single account → auto-select silently (picker-contract behavior); multiple → AskUserQuestion.
- Output: `ACCOUNT_JIRA_TOKEN_KEY`, `ACCOUNT_JIRA_HOST`.
- Token missing/expired → Token Save Flow from `setup.md` inline; user skips → abort, nothing created.

### [2/12] Resolve project key

Fallback chain (same as `$HOME/.claude/multi-agent-refs/issue-jira-triad.md` Create path):
1. `figmaConfig.jira.projectKey` (per-project config, when running inside a configured repo)
2. `prefs.global.defaultJiraKey`
3. Fetch and ask:
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://$ACCOUNT_JIRA_HOST/rest/api/2/project" \
  | python3 -c 'import json,sys; [print(p["key"]+"\t"+p["name"]) for p in json.load(sys.stdin)]'
```
   AskUserQuestion (single-select) → cache the choice to `prefs.global.defaultJiraKey`.
- Output: `PROJECT_KEY`.

### [3/12] Pick issue type

AskUserQuestion (single-select, `question` in `outputLanguage`, e.g. tr: "Ne oluşturmak istiyorsun?"): `Task` / `Bug` / `Story`. Never inferred silently.
- Output: `ISSUE_TYPE`. Selects the standard template from the table above.
- The chosen type drives the mining JQL in [5/12], the required-field discovery in [5/12], and the section skeleton in [7/12].

### [4/12] Parse input

- Any `figma.com` URL in `$ARGUMENTS` → `FIGMA_URL`. Node-id normalization: `-` → `:`. Branch URLs use `branchKey` as effective `fileKey` (see `rules/figma-pipeline.md` URL parsing).
- Any OpenAPI/Swagger URL (path or query contains `swagger`, `openapi`, `/v2/api-docs`, `/v3/api-docs`, or a `.json`/`.yaml` spec) → `SWAGGER_URL`.
- Pasted images (chat attachments) → `USER_SCREENSHOTS[]`. Pasted log/stack-trace text → kept with `FREE_TEXT` and later mapped to the Screenshots / Logs section (Bug) or Notes.
- Everything else → `FREE_TEXT`.
- All of `FREE_TEXT`/`FIGMA_URL`/`SWAGGER_URL`/`USER_SCREENSHOTS` empty → AskUserQuestion (free-text, in `outputLanguage`): "Describe the {type} you want to open."
- `FIGMA_URL`, `SWAGGER_URL`, and screenshots are always optional. Never ask for them; only use them when given.

### [5/12] Convention mining + field discovery + sprint detection (read-only)

Three GET groups. Run them before drafting anything.

**5a. Sample recent same-type issues:**
```bash
JQL="project = ${PROJECT_KEY} AND issuetype = ${ISSUE_TYPE} ORDER BY created DESC"
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://$ACCOUNT_JIRA_HOST/rest/api/2/search?jql=$(printf '%s' "$JQL" | python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read()))')&fields=summary,description,labels,components,priority,fixVersions,issuelinks&maxResults=30"
```
Extract from the sample (thresholds are guidance, not hard gates):
- **Summary prefix pattern**: leading `[...]` or `AREA:` token frequency. Appears in >= 40% of samples → adopt as suggested prefix; top 2-3 prefixes become AskUserQuestion options in step 8 when ambiguous.
- **Top labels / components**: top 5 each by frequency → option lists for step 8.
- **Priority norm**: modal priority for this issuetype → pre-filled default, editable in preview. (Bug priorities often skew higher  -  respect the project's own norm.)
- **Epic usage rate**: share of sampled issues carrying an epic link → decides whether step 8 asks for an epic.
- **Test-scenario style** (Task / Story, and Bug regression): detect how the project expresses test scenarios in the sampled descriptions and links, in priority order:
  1. Linked test issues  -  `issuelinks` pointing at a `Test`-family issuetype (Xray / Zephyr). If present, the standard Test Scenarios section references those linked tests rather than inlining steps.
  2. A dedicated description heading (`Test Scenarios`, `Test Cases`, `QA`, `Senaryolar`, checklist blocks). If a style is shared by >= 40% of samples, reuse that exact format (heading name + bullet/checklist/table shape).
  3. No prior style → inline a Test Scenarios skeleton derived from the Acceptance Criteria (one scenario per criterion, Given/When/Then or the project's dominant shape). Skeleton only  -  the user edits it in preview; pass/fail data is never fabricated.

**5b. Field discovery (createmeta):**
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://$ACCOUNT_JIRA_HOST/rest/api/2/issue/createmeta?projectKeys=${PROJECT_KEY}&issuetypeNames=${ISSUE_TYPE}&expand=projects.issuetypes.fields"
```
- 404 or empty `projects[]` (newer Jira DC deprecates the classic form) → fall back to the paged endpoint: `GET /rest/api/2/issue/createmeta/{PROJECT_KEY}/issuetypes` then `GET /rest/api/2/issue/createmeta/{PROJECT_KEY}/issuetypes/{issuetypeId}`.
- Every field with `required: true` beyond project/issuetype/summary/description/reporter → step 8 question (options from `allowedValues` when present, free-text fallback).
- Generic custom-field detection by `schema.custom` id  -  never assume field ids:
  - Sprint: `com.pyxis.greenhopper.jira:gh-sprint`
  - Epic Link: `com.pyxis.greenhopper.jira:gh-epic-link`
  - Team-like fields: case-insensitive name match on `Team`
- 403 → warn "cannot read project field metadata", continue with template defaults; required fields surface reactively via the 400 path in step 10.

**5c. Board + active sprint (Agile REST):**
```bash
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://$ACCOUNT_JIRA_HOST/rest/agile/1.0/board?projectKeyOrId=${PROJECT_KEY}"
curl -s -H "Authorization: Bearer $TOKEN" \
  "https://$ACCOUNT_JIRA_HOST/rest/agile/1.0/board/${BOARD_ID}/sprint?state=active"
```
- Multiple boards → AskUserQuestion (board name + type). One board → silent. Zero boards or Agile API 404 → the sprint option is silently omitted; the preview notes "backlog only (no board found)".
- Active sprint found → remember `SPRINT_ID` + name for the step 8 placement choice. None → backlog only; if a `state=future` sprint exists, mention it in the preview as information.

### [6/12] External context (only for the sources actually provided)

Each source is failure-isolated and never blocks issue creation.

**6a. Figma** (only when `FIGMA_URL` is set):
Follow the 3-tier chain from `rules/figma-pipeline.md`: Tier 1 MCP (`get_design_context` / `get_screenshot`, one re-auth retry) → Tier 2 REST (`keychainMapping.figma`) → Tier 3 ask the user for a screenshot or proceed link-only.

> This command is standalone (same class as `/multi-agent:analysis`), so Figma MCP use here is allowed  -  it is not a dev-phase violation under the "No MCP outside analysis phase" rule.

- Output: frame name, node id, optional screenshot PNG in `/tmp/generate-issue-$$-figma.png` for the step 11 attachment opt-in.
- Any tier failure degrades gracefully; link-only is always acceptable.

**6b. Swagger / API contract** (only when `SWAGGER_URL` is set or a contract snippet was pasted):
- `SWAGGER_URL` set → fetch the spec, then extract **only the referenced endpoints**: if `FREE_TEXT` names specific paths / operationIds / tags, extract those; otherwise summarize the single tag or path group the user pointed at. Never crawl or dump the whole spec.
```bash
curl -s "$SWAGGER_URL" -o /tmp/generate-issue-$$-swagger.json  # add auth header only if the user supplied one
```
  Parse with `python3`/`jq` to pull, per referenced endpoint: HTTP method + path, a one-line summary, and the key request/response fields (names + types, not the full schema).
- Only a pasted contract snippet → summarize that snippet (endpoints + key fields).
- Output: an `API Contract` section body: `{METHOD} {path}` + summary + key request/response fields, plus `[Swagger|{SWAGGER_URL}]` when a URL was given.
- Fetch failure / unreachable / no matching endpoint → degrade to link-only (or omit the section if nothing usable) and note it in the preview. Never block issue creation.

**6c. Screenshots**: `USER_SCREENSHOTS[]` from step 4 are staged for the step 11 attachment opt-in; each attached image is referenced inline in the relevant section with Jira wiki `!filename|thumbnail!` markup.

### [7/12] Compose draft

Write summary + description in `outputLanguage` from the type's **standard template** (baseline, not mined headings):
- **Summary**: `{minedPrefix} {concise title}`, truncated to 255 chars.
- **Description**: render the type's standard section skeleton in Jira wiki markup (`h3.` headings), applying auto-sizing:
  - **Always-present** sections are filled from `FREE_TEXT` (+ mining for Test Scenarios style). If an always-present section has no user content and cannot be derived, it stays an explicit open question for step 8  -  it is not fabricated.
  - **Conditional** sections render only when their trigger fired (see the Standard templates table): Design Reference when `FIGMA_URL` set (`[Figma|{FIGMA_URL}]` + frame name + node id); API Contract when 6b produced a body; Screenshots when images were staged; Notes/Dependencies only with real content. Otherwise the heading is omitted entirely.
  - Bug: logs / stack traces / device+OS mentions from `FREE_TEXT` map into Environment and Screenshots / Logs; anything underivable becomes a step-8 question.
- Run the `ai-common-toolkit:humanizer` skill on the description body.
- Write the final body to `/tmp/generate-issue-$$.txt` (UTF-8, real newlines) for the `--rawfile` POST.

### [8/12] Clarifying questions (only genuinely unknown fields)

Batched AskUserQuestion(s), each with a "Skip / leave unset" option where Jira allows it:
- **Component**: mined top components + "none"  -  only when the project uses components.
- **Epic link**: only when 5a epic usage rate is high. Options from `project = {KEY} AND issuetype = Epic AND statusCategory != Done`.
- **Priority**: only when the mined norm is ambiguous; otherwise pre-fill the norm and let the preview edit change it.
- **Labels**: mined top labels, multiSelect.
- **Assignee**: "me / unassigned / someone else".
- **Sprint placement**: "Active sprint: {name}" vs "Backlog"  -  only when an active sprint exists.
- **Required custom fields** from 5b not yet resolved (allowedValues as options).
- **Always-present sections still empty**: Bug Steps to Reproduce / Environment, or a Task/Story with no derivable Scope / Acceptance Criteria → ask here.

Rule: never ask about anything already answerable from mining or the input. Regardless of how complete the draft looks, step 9 always runs.

### [9/12] Full preview + approval gate (never skipped)

Render the complete issue in chat: project, issuetype, summary, the full description body, priority, labels, components, epic, assignee, sprint/backlog placement, attachment plan (Figma + user screenshots upload  -  default off, opt-in).

Then AskUserQuestion (`question` in `outputLanguage`, e.g. tr: "Bu sekilde olusturuyorum, onayliyor musun?"):

| Option | Behavior |
|---|---|
| `Approve` | Proceed to step 10 |
| `Edit` | Free-text "what should change?" → apply → re-render the FULL preview → re-ask. Loop, no iteration cap |
| `Cancel` | Stop. Nothing was created; say so explicitly |

This gate has no bypass. No flag, mode, or preference suppresses it.

### [10/12] Create

```bash
node "$HOME/.claude/scripts/jira-wiki-escape.mjs" /tmp/generate-issue-$$.txt \
  > /tmp/generate-issue-$$-escaped.txt
jq -n --rawfile desc /tmp/generate-issue-$$-escaped.txt \
  --arg key "$PROJECT_KEY" --arg type "$ISSUE_TYPE" --arg summary "$SUMMARY" \
  '{fields: {project: {key: $key}, issuetype: {name: $type}, summary: $summary, description: $desc}}' \
  > /tmp/generate-issue-$$-payload.json
# merge approved optional fields (priority / labels / components / epic / assignee / required customs) into .fields with jq before POST
curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data-binary @/tmp/generate-issue-$$-payload.json \
  "https://$ACCOUNT_JIRA_HOST/rest/api/2/issue"
```
- Parse `key` from the response → `NEW_KEY`.
- 400 with field errors → show Jira's per-field error verbatim, return to step 8 for exactly those fields, then re-run step 9 (full preview again).

### [11/12] Post-create steps

Each step is failure-isolated: on error, warn + continue  -  the issue already exists.

1. **Sprint** (when chosen in step 8/9):
```bash
jq -n --arg key "$NEW_KEY" '{issues: [$key]}' > /tmp/generate-issue-$$-sprint.json
curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  --data-binary @/tmp/generate-issue-$$-sprint.json \
  "https://$ACCOUNT_JIRA_HOST/rest/agile/1.0/sprint/${SPRINT_ID}/issue"
```
   Post-create move instead of a Sprint custom field in the create payload: field-id-agnostic, works on every Jira Software instance.
2. **Figma remote link** (when `FIGMA_URL` set):
```bash
curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"object":{"url":"'"$FIGMA_URL"'","title":"Figma design"}}' \
  "https://$ACCOUNT_JIRA_HOST/rest/api/2/issue/${NEW_KEY}/remotelink"
```
3. **Swagger remote link** (when `SWAGGER_URL` set):
```bash
curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"object":{"url":"'"$SWAGGER_URL"'","title":"API contract"}}' \
  "https://$ACCOUNT_JIRA_HOST/rest/api/2/issue/${NEW_KEY}/remotelink"
```
4. **Screenshot / image attachments** (only when opted in during preview  -  Figma render and/or user-pasted images):
```bash
curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "X-Atlassian-Token: no-check" \
  -F "file=@/tmp/generate-issue-$$-figma.png" \
  "https://$ACCOUNT_JIRA_HOST/rest/api/2/issue/${NEW_KEY}/attachments"
```

### [12/12] Report

```
Created {NEW_KEY}: https://{ACCOUNT_JIRA_HOST}/browse/{NEW_KEY}
```
Plus one line per post-create step that ran (sprint placement, remote links, attachments) and any that were skipped with reason.

## Error paths

| Condition | Behavior |
|---|---|
| Token missing/expired (401) | Token Save Flow from `setup.md` inline; user skips → abort with message, nothing created |
| Search 403 / createmeta denied | Warn "cannot read project conventions/fields"; continue with standard-template defaults; required fields asked reactively on 400 |
| Zero sampled issues (new project) | Skip mining, use standard template + AC-derived test-scenario skeleton, note in preview "no prior issues to learn from" |
| Agile API 404 / no boards | Omit sprint choice, backlog implied, say so in preview |
| No active sprint | Offer backlog only; mention the next `state=future` sprint when one exists |
| Figma Tier 1 fails | Tier 2; Tier 2 fails → Tier 3 (ask screenshot or link-only). Never block issue creation on Figma |
| Swagger fetch fails / unreachable | Degrade to link-only, or omit the API Contract section if nothing usable; note in preview. Never block issue creation |
| Create 400 (field errors) | Show per-field errors, loop back to step 8 for those fields, re-preview |
| Create 5xx / network | Show the error, offer one retry, then abort  -  keep `/tmp/generate-issue-$$.txt` and print its path so the draft survives |
| Post-create step failure | Warn + continue (issue exists); print a manual-fix hint |
