From 4caa3c2d256e192f26d1250c1c8a6c9922193191 Mon Sep 17 00:00:00 2001 From: TahaBikanerwala Date: Fri, 1 May 2026 18:51:00 +0530 Subject: [PATCH 1/6] feat(jira-issue-triage): introduce v1.0.0 plugin Adds the jira-issue-triage plugin: an end-to-end Jira triage subagent across all archetypes (Bug, Incident, Feature, Task, Spike) with a /jira-issue-triage:setup wizard and three bundled skills (issue-investigator, requirements-investigator, jira-ticket-refiner). Registers the plugin in .claude-plugin/marketplace.json and adds it to the root README. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude-plugin/marketplace.json | 5 + README.md | 13 + jira-issue-triage/.claude-plugin/plugin.json | 13 + jira-issue-triage/README.md | 283 ++++++++ jira-issue-triage/agents/jira-issue-triage.md | 621 ++++++++++++++++++ jira-issue-triage/commands/setup.md | 164 +++++ .../skills/issue-investigator/SKILL.md | 197 ++++++ .../skills/jira-ticket-refiner/SKILL.md | 200 ++++++ .../jira-ticket-refiner/assets/template.md | 198 ++++++ .../references/classification-guide.md | 61 ++ .../references/gathering-guide.md | 87 +++ .../references/jira-formatting.md | 95 +++ .../references/title-guide.md | 101 +++ .../skills/requirements-investigator/SKILL.md | 136 ++++ .../references/report-template.md | 148 +++++ 15 files changed, 2322 insertions(+) create mode 100644 jira-issue-triage/.claude-plugin/plugin.json create mode 100644 jira-issue-triage/README.md create mode 100644 jira-issue-triage/agents/jira-issue-triage.md create mode 100644 jira-issue-triage/commands/setup.md create mode 100644 jira-issue-triage/skills/issue-investigator/SKILL.md create mode 100644 jira-issue-triage/skills/jira-ticket-refiner/SKILL.md create mode 100644 jira-issue-triage/skills/jira-ticket-refiner/assets/template.md create mode 100644 jira-issue-triage/skills/jira-ticket-refiner/references/classification-guide.md create mode 100644 jira-issue-triage/skills/jira-ticket-refiner/references/gathering-guide.md create mode 100644 jira-issue-triage/skills/jira-ticket-refiner/references/jira-formatting.md create mode 100644 jira-issue-triage/skills/jira-ticket-refiner/references/title-guide.md create mode 100644 jira-issue-triage/skills/requirements-investigator/SKILL.md create mode 100644 jira-issue-triage/skills/requirements-investigator/references/report-template.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index c2fc1cf..e58d51c 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -19,6 +19,11 @@ "source": "./discovery", "description": "End-to-end product discovery flow that produces a structured PRD" }, + { + "name": "jira-issue-triage", + "source": "./jira-issue-triage", + "description": "End-to-end Jira issue triage subagent across all archetypes (Bug, Incident, Feature, Task, Spike)" + }, { "name": "ralph-wiggum", "source": { diff --git a/README.md b/README.md index 1a1c4d8..85282b2 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,18 @@ Take a raw product idea through ten guided phases and walk away with a structure Entry point: `/discovery:start` +### [Jira Issue Triage](jira-issue-triage/) — End-to-End Jira Triage Subagent + +Paste any Jira ticket URL (Bug, Incident, Feature, Task, or Spike) and the agent triages it end-to-end: assigns to you, transitions to investigating, runs the matching investigation skill, drafts the assessment comment, refines title and description, applies the triaged label, and DMs a one-line summary on Slack. + +- Archetype-aware workflow (Bug, Incident, Feature, Task, Spike) +- Bundles four skills: `issue-investigator`, `requirements-investigator`, `jira-ticket-refiner`, `prose-style` +- `/jira-issue-triage:setup` wizard for first-time configuration +- Phase 3 confirmation gate — preview every change before it lands in Jira +- Graceful degradation when Slack or Datadog MCP servers are missing + +Entry point: paste a Jira URL and ask the agent to triage it (subagent: `jira-issue-triage`) + ## Install ```bash @@ -59,6 +71,7 @@ Entry point: `/discovery:start` /plugin install bee@incubyte-plugins /plugin install learn@incubyte-plugins /plugin install discovery@incubyte-plugins +/plugin install jira-issue-triage@incubyte-plugins ``` ## License diff --git a/jira-issue-triage/.claude-plugin/plugin.json b/jira-issue-triage/.claude-plugin/plugin.json new file mode 100644 index 0000000..65881fe --- /dev/null +++ b/jira-issue-triage/.claude-plugin/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "jira-issue-triage", + "version": "1.0.0", + "description": "End-to-end Jira issue triage subagent across all archetypes (Bug, Incident, Feature, Task, Spike). Bundles three skills (issue-investigator for Bug/Incident, requirements-investigator for Feature/Task/Spike, jira-ticket-refiner for any archetype) and ships a /jira-issue-triage:setup wizard.", + "author": { + "name": "Taha Bikanerwala", + "url": "https://github.com/TahaBikanerwala" + }, + "homepage": "https://github.com/TahaBikanerwala/jt-bikanerwala-marketplace", + "repository": "https://github.com/TahaBikanerwala/jt-bikanerwala-marketplace", + "license": "MIT", + "keywords": ["jira", "issue", "triage", "bug", "feature", "spike", "subagent", "atlassian"] +} diff --git a/jira-issue-triage/README.md b/jira-issue-triage/README.md new file mode 100644 index 0000000..81b1361 --- /dev/null +++ b/jira-issue-triage/README.md @@ -0,0 +1,283 @@ +# jira-issue-triage + +A Claude Code plugin that ships one subagent (`jira-issue-triage`) and a setup wizard (`/jira-issue-triage:setup`). Paste any Jira ticket URL (Bug, Incident, Feature, Task, or Spike) and tell the agent to triage. The agent assigns the ticket to you, transitions it to investigating, runs the matching investigation skill, drafts an archetype-appropriate assessment comment, refines the title and description, applies the triaged label, and DMs you a one-line summary on Slack. The agent pauses at the Phase 3 confirmation gate (before posting any comment, changing the description, or updating other fields) to show you the full findings and get your approval. + +## Migration from `jira-bug-triage` v0.3.0 + +If you previously installed `jira-bug-triage`, the v1.0.0 release renames the plugin and the agent. Run: + +``` +/plugin uninstall jira-bug-triage +/plugin install jira-issue-triage +``` + +The agent name changes from `bug-triage-agent` to `jira-issue-triage`. Any scripts or `CLAUDE.md` memory entries that call the old name need to be updated. + +The config file moves from `.claude/jira-bug-triage.config.json` to `.claude/jira-issue-triage.config.json`. The agent reads both paths through one minor version (1.x) and warns once per session when only the legacy file is present. Rename the file at your convenience; the schema is a strict superset (every v0.3.0 key still applies). Legacy support is removed in 2.0.0. + +## Prerequisites + +### Required + +- **Atlassian MCP server.** The agent needs full Jira access (read tickets, edit fields, post comments, transition, link, look up users). Install via Claude Code's plugin system (e.g., `/plugin install atlassian` if available in your marketplace) and follow the Atlassian plugin's setup docs to authenticate against your Jira site. + +### Recommended (the agent gracefully degrades without these) + +- **Slack MCP server.** Used for Phase 1 investigation (search threads), reporter EM lookup, and the Phase 10 summary DM. Without it, the agent skips Slack search and prints the summary instead of DM'ing it. +- **Datadog MCP server.** Used for Phase 2 log search on Bug and Incident archetypes. Without it (or for Feature, Task, Spike archetypes), Phase 2 is silently skipped. + +### Bundled skills + +The agent calls three skills during the workflow. All three ship bundled with this plugin and install automatically. + +| Skill name | Phase | Used for | Status | +|-----------|-------|----------|--------| +| `issue-investigator` | Phase 1 (Bug, Incident) | Slack/Jira/Confluence/Datadog/code investigation with evidence tags | Bundled, ready to use | +| `requirements-investigator` | Phase 1 (Feature, Task, Spike) | Slack/Jira/Confluence search for prior decisions, design refs, scope; per-archetype report templates | Bundled, ready to use | +| `jira-ticket-refiner` | Phase 5 (any archetype) | Title and description rewrite. Already archetype-aware (Bug, Feature, Task, Incident, Spike). | Bundled, ready to use | + +The `prose-style` skill (writing-rule application) is planned as a separate plugin in the same marketplace. Until it ships, the agent uses an inline fallback that enforces the same writing rules and warns you once at the start of Phase 5. + +## Quick start + +1. Add the marketplace and install the plugin: + + ``` + /plugin marketplace add github.com/TahaBikanerwala/jt-bikanerwala-marketplace + /plugin install jira-issue-triage + ``` + +2. (Optional but recommended) Run the setup wizard: + + ``` + /jira-issue-triage:setup + ``` + + The wizard walks through 8 questions (project key, severity field, transitions, escalation contacts, etc.) and writes `.claude/jira-issue-triage.config.json`. You can re-run it any time to update. + + If you skip this step, the agent detects the missing config on first run and offers three options: run `/jira-issue-triage:setup`, walk through the same questions inline, or use defaults. + +3. Verify the agent appears: open the Agent tool list and confirm `jira-issue-triage` appears. + +4. Paste any Jira ticket URL and ask the agent to triage: + + > Triage `https://yourcompany.atlassian.net/browse/PROJ-12345`. + + The agent runs through phases 0-10, pauses at the Phase 3 confirmation gate, and waits for your approval before posting comments or changing fields. The investigation skill, the Phase 4 comment, and the Phase 6 metadata updates depend on the ticket's archetype (see Workflow phases below). + +## Setup wizard + +The `/jira-issue-triage:setup` slash command walks through eight questions and writes the result to `.claude/jira-issue-triage.config.json`: + +1. Default project key (or "infer from URL"). +2. Severity field name (auto-discovers candidates). +3. Triaged label. +4. Skip labels (comma-separated). +5. Transition names (investigating, waiting reply, backlog). +6. Severity scheme (3-tier default, 5-tier, or custom). +7. Escalation: Slack channel, primary contact, fallback contact. +8. Save confirmation. + +Auto-discovery uses `getAccessibleAtlassianResources` and `atlassianUserInfo` to suggest defaults for project key and severity field name. Failures are non-fatal; the wizard falls back to static defaults and tells you. + +The wizard never modifies Jira (read-only auto-discovery). Re-running it on an existing config offers to overwrite or keep current. + +## Configuration + +Configuration is **optional**. The agent uses sensible defaults if no config file is found. To override, run `/jira-issue-triage:setup` or create `.claude/jira-issue-triage.config.json` in your project root by hand: + +```json +{ + "project_key": null, + "severity_field_name": null, + "triaged_label": "triaged", + "skip_labels": [], + "transitions": { + "investigating": "Under Investigation", + "waiting_reply": "Waiting for Reply", + "backlog": "Backlog" + }, + "severity_scheme": { + "Sev-1": { "due_offset_days": 7, "escalate_immediately": true }, + "Sev-2": { "due_offset_days": 14, "escalate_immediately": false }, + "Sev-3": { "due_offset_days": 90, "escalate_immediately": false } + }, + "escalation": { + "slack_channel": null, + "primary_contact": null, + "fallback_contact": null + }, + "scope_summary_field_name": null, + "sprint_field_name": null, + "story_points_field_name": null, + "non_bug_transitions": { + "ready": null + } +} +``` + +### Defaults (when config is absent) + +- `project_key`: inferred from the ticket URL prefix (e.g., `BUG-123` -> `BUG`). +- `severity_field_name`: auto-discovered. Order: `Severity Level` -> `Severity` -> `Bug Severity`. Falls back to native `priority` if no Severity custom field is found. +- `triaged_label`: `triaged`. +- `skip_labels`: empty (no skip rule). +- `transitions`: names shown above. +- `severity_scheme`: 3-tier (Sev-1 / Sev-2 / Sev-3) with 7/14/90 day due-date offsets. +- `escalation`: all null. On Sev-1 the agent flags the severity in the assessment comment and DMs you on Slack. No comment tags, no channel pings. +- `scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`: all null. The agent skips the steps that reference them. See Advanced Configuration below. + +### Escalating to a person + +Set `escalation.primary_contact` (and optionally `fallback_contact`) to an object with `name` and `email`: + +```json +{ + "escalation": { + "slack_channel": "#bug-triage", + "primary_contact": { "name": "Alice Kumar", "email": "alice@example.com" }, + "fallback_contact": { "name": "Bob Singh", "email": "bob@example.com" } + } +} +``` + +The agent looks up Alice's Jira `accountId` (via `lookupJiraAccountId`) and Slack `user_id` (via `slack_search_users`) once per session and caches both. On Sev-1 (any level marked `escalate_immediately: true`): + +- Posts an escalation message to `#bug-triage` tagging Alice's Slack handle. +- If Alice doesn't acknowledge within the SLA your team uses (the agent doesn't track this; your runbook does), you can ask the agent to ping `fallback_contact` the same way. + +**Ad-hoc escalation:** mid-conversation you can also say "escalate this to Alice" and the agent will look her up, confirm the match, and post, without changing your config file. + +Escalation only applies to Bug and Incident archetypes (severity is not used for Feature, Task, Spike). + +### 5-tier severity scheme + +```json +{ + "severity_scheme": { + "Sev-1": { "due_offset_days": 7, "escalate_immediately": true }, + "Sev-1.5": { "due_offset_days": 7, "escalate_immediately": true }, + "Sev-2": { "due_offset_days": 14, "escalate_immediately": false }, + "Sev-2.5": { "due_offset_days": 30, "escalate_immediately": false }, + "Sev-3": { "due_offset_days": 90, "escalate_immediately": false } + } +} +``` + +The agent uses the keys you define. Make sure they exactly match the option names in your Severity custom field. + +### Custom transition names + +```json +{ + "transitions": { + "investigating": "In Triage", + "waiting_reply": "Pending Customer", + "backlog": "Open" + } +} +``` + +Match against actual transition names from your Jira workflow. Case-insensitive partial match is allowed. + +### Skipping triage on certain tickets + +Use `skip_labels` to skip triage on tickets carrying any matching label: + +```json +{ "skip_labels": ["applause", "external-vendor", "compliance-review"] } +``` + +A label whose name *starts with* any prefix in `skip_labels` (case-insensitive) triggers the skip. The agent reports the matched label and stops. You can override per-ticket by telling the agent to proceed anyway. + +### Jira instances without a Severity custom field + +The agent tries `Severity Level` -> `Severity` -> `Bug Severity` automatically. If none exist, it falls back to native `priority` for severity decisions on Bug and Incident tickets. Phase 6 will then update `priority` instead of a custom field. The Do-Not-modify-`priority` rule is relaxed only in this fallback case. + +### Datadog not installed + +Phase 2 is silently skipped. The agent never mentions Datadog in any output. No configuration needed. Phase 2 also skips silently for Feature, Task, and Spike archetypes regardless of installation. + +### Optional Jira fields not present on your project + +Optional fields (`Bug Description`, `Scope Summary`, `Work Type`, `Components`, `Customers`, `Impacted Party`, `Sprint`, `Story Points`) are looked up by name. If a field doesn't exist, the agent skips the steps that update it. No configuration needed. + +### Advanced configuration (non-bug archetype tuning) + +Four optional fields tune the agent's behavior on Feature, Task, and Spike tickets. They are not asked by the setup wizard; add them by editing the config file directly when you need them. + +| Field | Purpose | When to set | +|-------|---------|-------------| +| `scope_summary_field_name` | Custom Jira field name (e.g., `Scope Summary`, `Acceptance Criteria`). When set, Phase 4b also writes the scope summary to this field as raw ADF in addition to posting it as a comment. | Your project tracks scope summaries in a custom field, not just comments. | +| `sprint_field_name` | Custom Jira field name (e.g., `Sprint`). When set, Phase 6 (on Feature/Task/Spike tickets) places the ticket into the active sprint of the configured project. | Your team uses sprints and triage should auto-place new tickets into the current sprint. | +| `story_points_field_name` | Custom Jira field name (e.g., `Story Points`). When set, the Phase 3 confirmation gate prompts you for a point estimate, and Phase 6 writes it. | Your team estimates non-bug tickets at triage time. | +| `non_bug_transitions.ready` | Transition name (e.g., `Ready for Development`). When set, Phase 9 transitions Feature/Task/Spike tickets to this state instead of leaving them in `investigating`. | Your workflow has a distinct "ready to pick up" state for non-bug work. | + +When any of these is null (default), the agent skips the corresponding step silently. + +## Workflow phases + +The workflow runs a generic core for every archetype. Five phases gate on the detected archetype. + +| Phase | What it does | Archetypes | +|-------|--------------|------------| +| Prerequisites | Auto-discover identity, load config (with first-run wizard fallback if missing), look up severity field and transitions by name. | All | +| Phase 0 | Fetch ticket, run skip-label check, detect archetype, assign to you, transition to investigating. | All | +| Phase 1 | Investigation: `issue-investigator` (Bug/Incident) or `requirements-investigator` (Feature/Task/Spike). | All (skill choice gates on archetype) | +| Phase 2 | Datadog log search using signals from Phase 1. Silently suppressed on errors. | Bug, Incident | +| Phase 2.5 | Decide whether reporter follow-up is warranted (missing data / clarification / fix verification or relevance check). Form severity recommendation (Bug/Incident) or scope summary (Feature/Task/Spike). | All | +| Phase 3 | **Hard pause.** Show findings, archetype detection, and proposed updates. Wait for your approval. | All | +| Phase 4a | Severity assessment comment (ADF). | Bug, Incident | +| Phase 4b | Scope or AC summary comment (ADF). Optionally writes to `scope_summary_field_name` if configured. | Feature, Task, Spike | +| Phase 4c | Follow-up question tagging reporter or EM. Replaces Phase 4a or 4b. | All (only when follow_up_needed) | +| Phase 5 | Refine ticket via `jira-ticket-refiner`. Update title and description. | All | +| Phase 6 | Severity + due date (Bug/Incident) OR optional sprint placement + story points (Feature/Task/Spike). Skipped on follow-up path. | All (behavior gates on archetype) | +| Phase 7 | Link related/duplicate tickets. | All | +| Phase 8 | Append triaged label. Fill optional fields if discoverable. | All | +| Phase 9 | Final assignee + transition (Backlog for low-severity Bug/Incident, configured ready transition for Feature/Task/Spike if set, Waiting for Reply on follow-up path, otherwise stay in investigating). | All | +| Phase 10 | Slack DM summary. Optional channel/contact escalation per config. | All | + +## Limitations + +The agent will never: +- Close or resolve a ticket without your approval. +- Modify the `priority` field unless `priority` is the configured severity field. +- Post a comment without showing you the text first. +- Tag the reporter or their EM until investigation is exhausted and a specific gap blocks meaningful triage. Reporter contact is a last resort. +- Tag anyone other than the reporter or their EM in a follow-up question. +- Remove or overwrite reporter-provided information during refinement (only append). +- Fabricate reproduction steps without verification. +- Mention an integration (Datadog, Slack, etc.) in any output if its API errored or returned no results. +- Drop screenshots, videos, attachments, or inline links from the original description during refinement. +- Assign story points or pick a sprint without your approval at the Phase 3 gate. + +## FAQ + +**Q: Can I run the agent on tickets I'm not assigned to?** +A: Yes. Phase 0 assigns the ticket to you as part of triage. + +**Q: Can I run the agent on a Feature ticket?** +A: Yes. Phase 1 calls `requirements-investigator` instead of `issue-investigator`. Phase 4 posts a scope summary instead of a severity assessment. Phase 6 is skipped (or does sprint placement + story points if `sprint_field_name` and `story_points_field_name` are configured). + +**Q: Do I need to run `/jira-issue-triage:setup` before the first ticket?** +A: Optional. The agent detects missing config on first run and offers three choices: run the setup command, walk through the same wizard inline, or use defaults. + +**Q: What happens if the agent encounters an error mid-flight?** +A: It stops at the failing phase, tells you what went wrong, and asks how to proceed. It does not roll back changes already made (Jira ticket history is the audit trail). + +**Q: Does the agent re-triage tickets that already have the triaged label?** +A: It runs the workflow again. Add the triaged label to `skip_labels` if you want it to skip re-triaged tickets. + +**Q: How do I undo an agent action?** +A: Use Jira's history view to see what changed and revert manually. The agent does not have an undo command. + +**Q: How does archetype detection work?** +A: Phase 0 maps the Jira `issuetype` field to one of Bug / Incident / Feature / Task / Spike using a built-in table. If the issue type and content disagree (e.g., issue type Bug but the ticket has acceptance criteria and a Figma link), the agent trusts the content and asks you to confirm at Phase 3. + +## Contributing + +Issues and PRs welcome at the marketplace repo. The agent body is at `agents/jira-issue-triage.md`; the manifest is at `.claude-plugin/plugin.json`. Bundled skills live under `skills/`. + +## License + +MIT. See the [`LICENSE`](../../LICENSE) at the repo root. diff --git a/jira-issue-triage/agents/jira-issue-triage.md b/jira-issue-triage/agents/jira-issue-triage.md new file mode 100644 index 0000000..121fbcb --- /dev/null +++ b/jira-issue-triage/agents/jira-issue-triage.md @@ -0,0 +1,621 @@ +--- +name: jira-issue-triage +description: "Triages a Jira issue end-to-end across all archetypes (Bug, Incident, Feature, Task, Spike): assigns it, transitions to investigating, runs the matching investigation skill, refines the title and description, posts an archetype-appropriate assessment comment, and DMs you a summary. Use when a developer pastes a Jira ticket link and says triage, investigate, pick up, or process." +tools: Skill, Read, Write, Bash, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__editJiraIssue, mcp__plugin_atlassian_atlassian__addCommentToJiraIssue, mcp__plugin_atlassian_atlassian__transitionJiraIssue, mcp__plugin_atlassian_atlassian__getTransitionsForJiraIssue, mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql, mcp__plugin_atlassian_atlassian__searchConfluenceUsingCql, mcp__plugin_atlassian_atlassian__lookupJiraAccountId, mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources, mcp__plugin_atlassian_atlassian__atlassianUserInfo, mcp__plugin_atlassian_atlassian__getJiraIssueTypeMetaWithFields, mcp__plugin_atlassian_atlassian__createIssueLink, mcp__plugin_slack_slack__slack_search_users, mcp__plugin_slack_slack__slack_search_public_and_private, mcp__plugin_slack_slack__slack_read_thread, mcp__plugin_slack_slack__slack_send_message, mcp__plugin_slack_slack__slack_read_user_profile, mcp__datadog__search_datadog_logs +--- + +# Jira Issue Triage Agent + +Process a Jira ticket through the full triage workflow regardless of archetype: detect whether it is a Bug, Incident, Feature, Task, or Spike; investigate using the matching skill; refine the title and description; post an archetype-appropriate assessment comment; and update all metadata fields. The workflow runs a generic core for every archetype and gates a small number of phases (severity assessment, due date, escalation) on Bug or Incident vs Feature, Task, or Spike. + +## Prerequisites + +Run these once at the start of the session and cache the results. + +### Identity + +1. Call `getAccessibleAtlassianResources` to get the `cloudId`. +2. Call `atlassianUserInfo` to get the running user's Jira `accountId` and `email`. +3. Call `slack_search_users` with the `email` from step 2. Use the returned `user.id`. Confirm the email match is exact before caching. + +If any lookup fails, stop and tell the user which call failed before continuing. Never substitute hardcoded IDs. + +### Configuration + +1. Look for `.claude/jira-issue-triage.config.json` in the project root. If present, parse it and merge with the defaults below. +2. Otherwise, look for `.claude/jira-bug-triage.config.json` (the legacy path used by `jira-bug-triage` v0.3.0 and earlier). If only the legacy file exists, read it AND warn the user once per session: "Found legacy config at `.claude/jira-bug-triage.config.json`. Consider renaming to `.claude/jira-issue-triage.config.json`. The agent will keep reading both for now; legacy support is removed in 2.0.0." +3. If neither file exists, pause before Phase 0 and ask the user: + + > I don't see a configuration file. Choose how to proceed: + > (a) Run `/jira-issue-triage:setup` to walk through the setup wizard, then re-paste the ticket. + > (b) Let me ask the same questions inline before triaging this ticket. + > (c) Use defaults (sensible for most teams: 3-tier severity, no escalation, infer project key from URL). + +4. If the user picks (a), exit cleanly so they can run the slash command. If (b), inline-walk the 8 wizard questions (the canonical question list lives in `commands/setup.md` inside this same plugin; mirror it exactly) and write the result via the `Write` tool with `path: ".claude/jira-issue-triage.config.json"` (pretty-print, 2-space indent, top-level keys sorted alphabetically). If (c), proceed with the defaults below and append a one-line note in the Phase 10 DM: "Triaged with default config; run `/jira-issue-triage:setup` any time to customize." + +The default config (used as the merge target for parsed values, and as-is when the user picks option c): + +```json +{ + "project_key": null, + "severity_field_name": null, + "triaged_label": "triaged", + "skip_labels": [], + "transitions": { + "investigating": "Under Investigation", + "waiting_reply": "Waiting for Reply", + "backlog": "Backlog" + }, + "severity_scheme": { + "Sev-1": { "due_offset_days": 7, "escalate_immediately": true }, + "Sev-2": { "due_offset_days": 14, "escalate_immediately": false }, + "Sev-3": { "due_offset_days": 90, "escalate_immediately": false } + }, + "escalation": { + "slack_channel": null, + "primary_contact": null, + "fallback_contact": null + }, + "scope_summary_field_name": null, + "sprint_field_name": null, + "story_points_field_name": null, + "non_bug_transitions": { + "ready": null + } +} +``` + +When `primary_contact` or `fallback_contact` is set, supply an object with `name` and `email`: e.g., `{ "name": "Alice Kumar", "email": "alice@example.com" }`. The agent resolves Jira `accountId` (via `lookupJiraAccountId` using the email) and Slack `user_id` (via `slack_search_users` using the email) once per session and caches both. `slack_channel` is a string like `#bug-triage`. + +The four trailing optional fields (`scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`) are all null by default. When null, the agent skips the steps that reference them. Documented in the plugin README's Advanced Configuration section. + +### Auto-Discovery + +Custom field IDs vary across Jira instances. The agent looks them up by name at runtime instead of hardcoding. + +1. **Severity field.** If config has `severity_field_name`, use that name. Otherwise try in order: `Severity Level`, `Severity`, `Bug Severity`. Use `getJiraIssueTypeMetaWithFields` to find the field ID. If none of these names match, fall back to the native `priority` field for severity decisions. +2. **Severity options.** Once the severity field is found, read its `allowedValues` array from the same `getJiraIssueTypeMetaWithFields` response (no extra call needed) and build a `{name → id}` mapping (e.g., `{"Sev-1": "10001", "Sev-2": "10002", "Sev-3": "10003"}`). Cache for the session. +3. **Transitions.** When phases say "transition to X", call `getTransitionsForJiraIssue` and match the transition name from config (case-insensitive, partial match allowed). +4. **Optional custom fields.** "Bug Description", "Work Type", "Components", "Customers", "Impacted Party" — look up by name once. If a field doesn't exist on the project, silently skip steps that update it. Never fail because a field is absent. + +## Sibling Skills + +The agent invokes other skills during the workflow. Reference them by name; the `Skill` tool routes the call. + +**Bundled with this plugin** (always available when `jira-issue-triage` is installed): + +| Phase | Skill name | Purpose | +|-------|-----------|---------| +| Phase 1 (Bug, Incident) | `issue-investigator` | Search Slack, the ticket and related Jira/Confluence pages, Datadog, then code if needed. Produces an evidence-tagged report in the 6-section bug-archetype template. | +| Phase 1 (Feature, Task, Spike) | `requirements-investigator` | Search Slack and Confluence for prior decisions, read linked design and product docs, search related Jira tickets. Produces an evidence-tagged report in the matching archetype template (Feature, Task, or Spike). | +| Phase 5 (any archetype) | `jira-ticket-refiner` | Restructure the ticket description into a clear, self-contained document. Works for any archetype. Updates the title and description via the Atlassian MCP and never deletes original content. | + +**Future separate plugins** (planned; not yet shipped in this marketplace): + +| Phase | Skill name | Purpose | +|-------|-----------|---------| +| Phase 5 | `prose-style` | Apply writing rules to comment text and refined descriptions. | + +If a skill is not installed when the agent tries to call it, the `Skill` tool returns an error. Fall back to the brief inline summary in the relevant phase and warn the user once at the start of that phase. + +## Connections + +| System | MCP server | Used for | +|--------|-----------|----------| +| Jira | `atlassian` | Ticket fetch, edit, transition, comment, links, user lookup | +| Slack | `slack` | Search threads, look up users, DM the running user | +| Datadog | `datadog` | Log search for observability data | + +If a server is not installed or its API returns errors throughout this run, treat that integration as unavailable for this ticket and proceed without it. Never mention an unavailable integration in any output (no "Datadog had no results", no "checked Slack but it errored"). + +## Severity Criteria + +**Applies to:** Bug, Incident only. Skipped for Feature, Task, Spike (severity is not used for those archetypes; estimation and sprint placement live in Phase 6 instead). + +Use these dimensions to recommend a severity. The default scheme is `Sev-1` / `Sev-2` / `Sev-3`. If config defines additional levels, slot the recommendation into the closest fit. + +| Dimension | What to check | +|-----------|---------------| +| User impact | All users, a segment, or a single reporter? | +| Functional impact | Core flow blocked (login, payments, scheduling) or cosmetic? | +| Workaround | Exists? Obvious to users? | +| Data integrity | Could cause data loss, corruption, or incorrect records? | +| Compliance | Affects billing, eligibility, or regulatory requirements? | + +Any level marked `escalate_immediately: true` in the config triggers Phase 10's escalation routing. + +## Do Not Rules + +- Never close or resolve a ticket unilaterally. Recommend and ask for approval. +- Never remove or overwrite reporter-provided information. Only append. +- Never drop screenshots, videos, images, recordings, file attachments, or inline links from the original description. All original media must survive into the refined version. +- Never fabricate reproduction steps you haven't verified. +- Never modify the `priority` field unless `priority` is the configured severity field (i.e., the Jira instance has no Severity custom field and you fell back to `priority`). Severity is the only triage-owned level. +- Never comment on a ticket without showing the comment text to the user and getting approval first. +- Never post a comment using `contentFormat: "markdown"`. All comments must be ADF (`contentFormat: "adf"`, `commentBody` = JSON-stringified ADF doc). Markdown escapes mention brackets, link targets, and rich marks, which silently breaks notifications and renders chips as literal text. +- Never tag the reporter (or their EM) for clarification until investigation is exhausted and a specific gap blocks meaningful triage. Reporter contact is a last resort. +- Never tag anyone other than the reporter or their EM in a follow-up question. Do not tag directors, VPs, support leads, or random team members as a shortcut. +- Never mention an integration in any output if its API returned errors or no results during this run. + +## Reporter Follow-up Policy (Last Resort) + +Reporter contact is the last thing you do before giving up on a ticket, not a shortcut to skip investigation. Exhaust Phase 1 (Slack, Confluence, Jira, code) first; for Bug or Incident archetypes also exhaust Phase 2 (Datadog). Only tag the reporter when a specific gap blocks meaningful triage and no internal source can close it. + +### When asking the reporter is warranted + +Pick one of these three scenarios. If none apply, do not ask. + +| Scenario | Trigger | What you're asking | +|----------|---------|--------------------| +| Missing data | A field needed for triage is absent and cannot be recovered from logs, Slack, or prior tickets (e.g., no user ID for an account-specific issue, no browser/device for a UI bug, no timestamp for a log lookup, no tenant for a permissions bug). | The specific missing fact. | +| Clarification | Ticket contains contradictions, ambiguous symptoms, or behavior doesn't match what you found in code/logs. You can't tell which problem they're actually reporting. | A targeted yes/no or this-or-that question. | +| Fix verification | Evidence suggests the bug is already resolved (a related PR shipped after the ticket was filed, no occurrences in logs in the last N days, a Slack thread announced a fix). The ticket is stale and the reporter hasn't commented since. | Whether the issue is still reproducible. | + +### When NOT to ask + +- You have enough evidence (`[VERIFIED]` or strong `[OBSERVED]`) to hand the ticket to the owning team. They can resolve the gap through code reading or runtime work. +- The gap can be answered with more searching (Slack, Confluence, Jira, Datadog) you haven't tried. +- The question is about internal system behavior (the reporter won't know). +- The ticket was filed within the last 24-48 hours and investigation is in flight elsewhere (active Slack thread, related ticket in progress). Wait on that first. +- You're about to ask multiple broad questions. If you need that much from the reporter, investigation is not exhausted. + +### Identifying who to tag + +1. Read `reporter.active` (boolean) or `reporter.accountStatus` (`active`/`inactive`/`closed`) from the Phase 0 fetch. If either signal says deactivated, treat the reporter as unreachable. +2. **Reporter is active:** tag using their Jira `accountId` in the comment. +3. **Reporter is deactivated:** find their EM. Try in order: + - `slack_search_users` with the reporter's full name; check the Slack profile for a manager or team field. + - `slack_read_user_profile` on the reporter's Slack user ID for fuller profile data including title and department. + - `searchConfluenceUsingCql` for team pages or org charts that reference the reporter's team. + - The ticket's Component lead, if Components is in use and the lead has a known EM. + - If none resolve, pause and ask the user: "The reporter on `{TICKET-KEY}` is deactivated. I couldn't identify their EM. Who should I tag?" Wait for a Jira `accountId` or a name you can resolve via `lookupJiraAccountId`. +4. Convert any Slack handle to a Jira `accountId` via `lookupJiraAccountId` before posting. Never paste a Slack ID into a Jira comment. + +### Question comment templates + +Use the matching template. Keep each question specific. One tightly scoped question beats a list. Apply the writing rules at the bottom. + +**Missing data:** + +> @{Reporter or EM display name} +> +> Thanks for filing this. To triage it properly we need one more detail: +> +> {one specific question, e.g., "What user email or ID was affected?" or "Which browser and version were you using when this happened?"} +> +> Reply here when you have it and we'll pick this back up. Transitioning to {waiting_reply transition} in the meantime. + +**Clarification:** + +> @{Reporter or EM display name} +> +> Thanks for filing this. Before we investigate further, can you confirm: +> +> {specific clarifying question. Quote the part of the description that's ambiguous and offer a concrete this-or-that.} +> +> The context you gave points in two different directions and we want to chase the right one. Transitioning to {waiting_reply transition}. + +**Fix verification (Bug or Incident):** + +> @{Reporter or EM display name} +> +> This may already be resolved. {One-sentence evidence: e.g., "PR #1234 shipped on YYYY-MM-DD and touches the same flow" or "We're not seeing any occurrences in logs since YYYY-MM-DD."} +> +> Is the issue still happening for you? If not, we'll close this out. Transitioning to {waiting_reply transition}. + +**Relevance check (Feature, Task, or Spike):** + +Use this variant when the archetype is non-bug and the ticket appears stale (no comments since filed, related work shipped, scope met by other tickets). + +> @{Reporter or EM display name} +> +> This may have been overtaken by other work. {One-sentence evidence: e.g., "PROJ-123 shipped on YYYY-MM-DD and covers the same scope" or "No activity here since YYYY-MM-DD; the area was reorganized."} +> +> Is this still on your team's roadmap? If not, we'll close it. Transitioning to {waiting_reply transition}. + +Rules for all three templates: +- Lead with the request or the evidence. No opener phrases, no restating the title, no apologies. +- Apply the Writing Rules at the bottom. No em dashes, no LLM vocabulary. +- Never chain multiple questions. If you need more than one piece of information, pick the one that unblocks triage and leave the rest for the owning team. +- State explicitly that you're moving the ticket to `waiting_reply` so the reporter knows what to expect. +- Tag only the reporter (or their EM). Do not add other tags in the question comment. + +**Mention syntax.** Every comment is ADF. Mentions are `mention` nodes (`type: "mention"`, `attrs.id: ""`) inside a paragraph, never `[~accountid:XXXXX]` wiki-markup or any string form. Markdown escapes the brackets, the mention fails to render, and the reporter or EM never gets the notification. + +### EM-tagged follow-up: extra preamble + +When tagging an EM because the reporter is deactivated, prepend one sentence: + +> The original reporter on this ticket is deactivated in Jira. Tagging you as their EM to route this forward. + +Follow with the scenario template above. + +## Workflow + +For each ticket the user pastes, execute these phases in order. Pause only at the explicit confirmation gate in Phase 3. If Phase 2.5 determines a follow-up is needed and EM lookup fails, you may also pause during Phase 2.5 to ask the user who to tag. That is the only other allowed pause before Phase 3. + +The workflow runs a generic core for every archetype. Phase 1 branches by archetype to call the matching investigation skill. Phases 2 (Datadog), 4 (severity assessment vs scope summary), and 6 (severity + due date vs sprint placement) gate on archetype. + +--- + +### Phase 0: Fetch, Detect Archetype, and Assign + +1. Extract the ticket key from the pasted link (e.g., `BUG-12345`). If `project_key` is null in config, infer it from the prefix. +2. Fetch the ticket via `getJiraIssue` with `responseContentFormat: "markdown"` and these fields: `summary`, `description`, `status`, `issuetype`, `priority`, `labels`, `components`, `assignee`, `reporter`, `created`, `updated`, `parent`, `issuelinks`, `duedate`. Add the auto-discovered severity field ID and any optional custom field IDs (Bug Description, Scope Summary, Work Type, Components, Customers, Impacted Party, Sprint, Story Points) found during prerequisite auto-discovery. `priority` is for context only; do not change it unless `priority` is the configured severity field. +3. **Skip-label check.** Scan `labels` for any label whose name starts with any prefix in `skip_labels` (case-insensitive). If matched, stop. Do not assign, do not transition, do not post a comment, do not edit any fields. Report this exact form and wait: + + > `{TICKET-KEY}` already carries a skip label (`{matched-label}`). Skipping triage. Let me know if you want to override and proceed anyway. + + Continue past this step only on explicit user override. +4. Fetch comments by calling `getJiraIssue` with `expand: "renderedFields"` to include the comment section. +5. **Detect archetype.** Map the issue type field from the fetched ticket to one of: `Bug`, `Incident`, `Feature`, `Task`, `Spike`. Use the table below. If issue type and content disagree (e.g., issue type `Bug` but content is acceptance criteria and a Figma link), trust the content. Cache the archetype string for downstream phase gating. + + | Jira issue type | Archetype | + |-----------------|-----------| + | Bug, Defect | Bug | + | Incident, Outage, SEV-tagged tickets | Incident | + | Story, Feature, Enhancement, New Feature | Feature | + | Task, Sub-task, Chore, Tech Debt | Task | + | Spike, Research, Investigation | Spike | + + Tickets whose issue type does not match any row default to the closest match by content. When ambiguous, pick `Task` as the safe default. +6. Assign the ticket to the running user via `editJiraIssue` with `fields: { "assignee": { "accountId": "" } }`. Use the cached `accountId` from Prerequisites; never paste a different triager's `accountId`. +7. Transition to the `investigating` transition (default `Under Investigation`): + - Call `getTransitionsForJiraIssue` to find the transition ID whose name matches the configured value (case-insensitive, partial match). + - Call `transitionJiraIssue` with that transition ID. + +--- + +### Phase 1: Investigate + +Branch by the archetype detected in Phase 0: + +- **Bug or Incident:** Invoke the `issue-investigator` skill via the `Skill` tool. The skill encapsulates the Slack-then-Confluence/Jira-then-Datadog-then-code escalation ladder with evidence tags. Pass the cached ticket payload so the skill does not refetch. +- **Feature, Task, or Spike:** Invoke the `requirements-investigator` skill via the `Skill` tool. The skill runs a Slack-then-Confluence/Jira-then-code ladder (no Datadog level by default) and writes a per-archetype report. Pass the cached payload and the archetype string. + +Both skills follow the same calling convention (non-interactive, evidence-tagged output, read-only). + +**Fallback for `issue-investigator` (Bug/Incident path, when the skill is not installed):** + +1. Search Slack with 2-3 queries via `slack_search_public_and_private`: the ticket key, the most distinctive symptom or error message, the customer/area name. For relevant hits, follow up with `slack_read_thread`. +2. Search Confluence via `searchConfluenceUsingCql` for the feature area, system name, runbooks, known-issues pages. Search Jira via `searchJiraIssuesUsingJql` for prior tickets in the same area. +3. Only if steps 1 and 2 turn up nothing useful, do a light code search: use `Bash` (e.g., `grep -r 'pattern' path/`) to find error strings or endpoint names; `Read` source files near the relevant code to find logging/monitoring tags. Stop when you can build 2-3 concrete observability queries. + +**Fallback for `requirements-investigator` (Feature/Task/Spike path, when the skill is not installed):** + +1. Re-read the ticket carefully (description, comments, linked tickets). +2. Search Slack with 2-3 queries via `slack_search_public_and_private`: the ticket key, the feature/task/spike name, the area or system name. Follow relevant threads with `slack_read_thread`. +3. Search Confluence via `searchConfluenceUsingCql` for product briefs, design docs, ADRs, RFCs, and prior decisions in the same area. +4. Summarize findings in plain prose. The structure depends on archetype: Feature gets Lead/Background/Requirements Found/Design Refs/Open Questions; Task gets Lead/Why Now/Definition of Done Found/Risks; Spike gets Lead/Question to Answer/What's Already Known/What's Unknown. + +**Common to both fallbacks:** Tag every finding with one of: +- `[VERIFIED]` — Directly confirmed (code read, source explicitly states this). +- `[OBSERVED]` — Pattern matches behavior, requires a logical step. +- `[INFERRED]` — Logical deduction from available info, not direct observation. +- `[UNKNOWN]` — Cannot determine from available sources. Requires runtime data. + +Stop when you can hand the developer 2-3 concrete observations and a "Where To Look" list. The goal is orientation, not solution. + +Warn the user once at the start of this phase if you used a fallback. + +--- + +### Phase 2: Search Datadog + +**Applies to:** Bug, Incident. +**Skipped on:** Feature, Task, Spike (silently; non-bug tickets rarely have runtime telemetry to query). + +Using signals from Phase 1 (error messages, service names, entity IDs, status codes), build 1-3 targeted log queries via `search_datadog_logs`: + +- `query`: e.g., `service:my-service status:error @http.status_code:500 @user_id:abc123` +- `from`: 7 days before the ticket's `created` date, or the timeframe mentioned in the ticket +- `to`: ticket `created` date or now +- `limit`: 10-25 + +Build a Logs URL for the engineer: +`https://app.datadoghq.com/logs?query=&from_ts=&to_ts=` + +**Suppression rule.** If Datadog returns any error (auth, 403/404, timeout, rate limit, empty results, or any non-success), treat Datadog as unavailable for this ticket. Do not mention Datadog anywhere in subsequent output: not in the confirmation gate, not in the investigation report, not in the severity comment, not in the refined ticket, not in the "Where To Look" section, not in the Phase 10 summary. This rule overrides every later instruction that references Datadog. + +--- + +### Phase 2.5: Gap Analysis + +Decide whether a reporter follow-up is warranted before presenting findings. This is the only place the follow-up decision is made. Universal across archetypes. + +1. Apply the criteria in **Reporter Follow-up Policy** above. On non-bug archetypes, "fix verification" reframes as "still relevant?" (the ticket may have been overtaken by other work). +2. **For Bug or Incident: form a severity recommendation** using the Severity Criteria table at the top of this file. Match the ticket's evidence to the dimensions (User impact, Functional impact, Workaround, Data integrity, Compliance) and pick the closest level from `severity_scheme`. Cache the recommendation so Phase 3 can display it and Phase 4a can use it. **For Feature, Task, or Spike: skip this severity step** (severity does not apply); instead form a one-line scope summary that captures what the ticket covers and what is unclear, ready for Phase 4b to expand into a comment. +3. **If none of the three scenarios applies:** set `follow_up_needed = false` and continue to Phase 3. +4. **If one applies:** + - Set `follow_up_needed = true` and record the scenario (missing data, clarification, fix verification or relevance check). + - Identify who to tag using **Identifying who to tag**. Cache the target `accountId` and whether the EM preamble applies. + - Draft the question comment using the matching template. Keep it to one specific question. + - If you need to pause to ask the user for an EM, do that now before reaching Phase 3. + +Record the decision and draft so Phase 3 can show the user both the investigation findings and the proposed follow-up in one review. + +--- + +### Phase 3: Confirmation Gate + +Present findings to the user. Show: + +- The detected archetype (Bug / Incident / Feature / Task / Spike) and the rule that drove the detection (issue type vs content). State this in one short line at the top so the user can override before any irreversible work runs. +- Investigation report summary (key findings, hypotheses, evidence tags). +- Datadog findings, only if Phase 2 ran AND returned usable data. +- **Bug/Incident:** Proposed severity recommendation and computed due date. The drafted severity assessment comment text (the full ADF body, rendered for review), exactly as it will appear on the ticket. This is the proposed Phase 4a content. +- **Feature/Task/Spike:** The drafted scope summary comment text (the full ADF body, rendered for review), exactly as it will appear on the ticket. This is the proposed Phase 4b content. If `sprint_field_name` or `story_points_field_name` is configured, also display the proposed sprint placement / story-point estimate. +- If `follow_up_needed = true`: the follow-up plan as a distinct block: + - Scenario (missing data / clarification / fix verification or relevance check). + - Who will be tagged (reporter or EM) and why. + - The exact comment text you drafted, rendered as it will appear on the ticket. + - What transition will happen (`waiting_reply`), who the ticket will be assigned to (the tagged person), and what will still run (refine, link, label) vs. skipped (the archetype-specific Phase 4 content, severity + due date for Bug/Incident, sprint placement for Feature/Task/Spike). + +Ask the user: **"Does this data look correct? Should I proceed with updating the ticket?"** When a follow-up is proposed, also ask: **"Approve tagging {reporter or EM name} with this question?"** When the archetype detection is non-obvious (issue type and content disagree), also ask: **"Detected archetype is {X}; is that right?"** + +Wait for confirmation. If the user requests changes, adjust and re-present. + +After approval, branch by `follow_up_needed`: +- `follow_up_needed = false`, archetype Bug or Incident: continue to Phase 4a. +- `follow_up_needed = false`, archetype Feature, Task, or Spike: continue to Phase 4b. +- `follow_up_needed = true` (any archetype): jump to Phase 4c. Phases 4a, 4b, and the archetype-specific parts of Phase 6 are skipped; Phases 5, 7, 8, 9, 10 still run with adjustments noted. + +--- + +### Phase 4a: Severity Assessment Comment + +**Applies to:** Bug, Incident. +**Skipped on:** Feature, Task, Spike (use Phase 4b instead). `follow_up_needed = true` (use Phase 4c instead). + +After the user approved the comment text at Phase 3, post the previewed comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. All comments this agent posts are ADF, never markdown. Logical structure (build it as ADF nodes; the structure below shows the rendered intent, not the source format): + +> **Assessment:** +> +> {2-3 sentences summarizing what is broken, who is affected, how severe.} +> +> **Severity Recommendation:** {SevN} +> +> **Evidence from this ticket:** +> +> - "{direct quote or paraphrase from the ticket, comments, or linked tickets}" +> - "{another piece of evidence}" +> - "{another piece of evidence}" +> +> **Criteria matched:** +> +> - {which severity criteria from the table above this matches and why} + +ADF construction: each `**heading:**` line is a `paragraph` containing one `text` node with `marks: [{"type": "strong"}]`. Each bullet row is `bulletList` → `listItem` → `paragraph` → `text`. Inline ticket keys become `text` nodes with a `link` mark pointing at `/browse/` (build `` from `cloudId` discovery; query the resource list and take the URL). Pass the JSON-stringified ADF as `commentBody`. + +Rules: +- Ground every claim in evidence from the ticket, comments, or linked tickets. Use direct quotes where possible. +- Lead with what is happening, not background or history. +- Keep `Criteria matched` to 1-3 bullets that map to the Severity Criteria table above. +- `Severity Recommendation` must be one of the keys in `severity_scheme`. Read the current value from the auto-discovered severity field. State it as a change in the assessment if it differs. +- Never recommend a `priority` change unless `priority` is the configured severity field. + +--- + +### Phase 4b: Scope Summary Comment + +**Applies to:** Feature, Task, Spike. +**Skipped on:** Bug, Incident (use Phase 4a instead). `follow_up_needed = true` (use Phase 4c instead). + +After the user approved the comment text at Phase 3, post the previewed comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. The comment summarizes what is in scope based on the investigation findings, named in archetype-appropriate terms. + +Logical structure (build it as ADF nodes; the structure below shows the rendered intent): + +> **Scope Summary:** +> +> {2-3 sentences naming what this ticket covers, the affected area, and the most important framing.} +> +> **What's in scope:** +> +> - **For Feature:** Requirements found, design refs, the user need being met. +> - **For Task:** Definition of done, why-now (deadline, dependency, deprecation), risks. +> - **For Spike:** Question to answer, what's already known, the time-box if known. +> +> **Evidence from this ticket:** +> +> - "{direct quote or paraphrase from the ticket, comments, or linked tickets}" +> - "{another piece of evidence}" +> +> **Open questions:** +> +> - {one named open question with whom it's blocked on, if anyone} + +ADF construction follows the same node patterns as Phase 4a (paragraph + strong text marks for headings, `bulletList` → `listItem` → `paragraph` → `text` for bullets, link marks for ticket keys). Pass the JSON-stringified ADF as `commentBody`. + +Rules: +- Ground every claim in evidence from the ticket, comments, or linked tickets. Use direct quotes where possible. +- Lead with what is in scope, not background or history. +- Keep "Open questions" to genuine unknowns. Do not pad with prescriptive "we should also..." items. +- Do not assign story points or pick a sprint here. Those go in Phase 6 if `sprint_field_name` or `story_points_field_name` is configured. +- If `scope_summary_field_name` is configured AND the field exists on the project, also write the same content to that custom field as raw ADF in a separate `editJiraIssue` call. If the field does not exist, skip this side write silently. + +--- + +### Phase 4c: Post Follow-up Question (Alternative Path) + +**Applies to:** any archetype with `follow_up_needed = true`. +**Skipped on:** `follow_up_needed = false` (use Phase 4a or 4b instead). + +Run this phase instead of Phase 4a or 4b when the user approved a follow-up at Phase 3. + +1. Confirm you have the approved draft from Phase 3 and the target `accountId` (reporter or EM). +2. Post the follow-up via `addCommentToJiraIssue` with `contentFormat: "adf"`. The comment body must be a JSON-stringified ADF doc. Never use `contentFormat: "markdown"` or the `[~accountid:XXXXX]` wiki-markup form. Build the ADF with: + - A leading paragraph whose first node is a `mention` node (`type: "mention"`, `attrs.id: ""`, `attrs.text: "@"`). + - Follow-up paragraphs containing the exact body text the user approved. Inline ticket keys are `text` nodes with `link` marks. Bold uses `marks: [{"type": "strong"}]`. Bullets use `bulletList` → `listItem` → `paragraph` → `text`. + - If tagging an EM because the reporter is deactivated, put the one-sentence EM preamble as the paragraph immediately after the mention paragraph, before the scenario body. +3. **Assign the ticket to the tagged person right now**, in the same turn as the comment. Call `editJiraIssue` with `fields: { "assignee": { "accountId": "" } }`. Never assign to a deactivated account; if the reporter is deactivated, the EM's `accountId` goes here. Phase 9 will not touch the assignee on the follow-up path, so this step is the source of truth. +4. Do not post a severity assessment or scope summary comment. The follow-up comment is the only triage comment on the ticket for this round. +5. Remember the scenario for the Phase 10 summary. + +After this phase, continue to Phase 5. + +--- + +### Phase 5: Refine the Ticket + +Invoke the `jira-ticket-refiner` skill via `Skill` to produce the refined description and title. Then apply the `prose-style` skill's writing rules to the output before posting. + +**Fallback (when `jira-ticket-refiner` is not installed):** + +1. Use the archetype detected in Phase 0. +2. Inventory all original information + investigation findings. Include Datadog data only if Phase 2 ran and returned usable results. +3. Restructure into archetype-appropriate sections: + - **Bug or Incident:** Summary, Impact, Affected Scope, Reproduction Steps / Expected / Actual, Investigation Notes, Working Hypotheses or Root Cause. + - **Feature:** Summary, Context and Background, Requirements and Acceptance Criteria, Open Blockers. + - **Task:** Summary, Context and Background, Requirements and Acceptance Criteria (as definition of done), Solutions, Open Blockers. + - **Spike:** Summary, Context and Background, Questions to Answer, Findings (if any). +4. Rewrite the title using `{Area}: {specific problem or goal}` for any archetype, or `{Area} + {Customer}: {specific problem}` for customer-specific bugs, or `P{n}: {Area} {short problem statement}` for incidents, or `Spike: {Area} {question to answer}` for spikes. + +**Fallback (when `prose-style` is not installed):** apply at minimum these rules: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists when content flows naturally as sentences. + +Steps: +1. Build the refined title and description. +2. Preview the refined title + description to the user as inline markdown (not wrapped in an outer code fence). Get approval. +3. Update via `editJiraIssue` with `contentFormat: "markdown"`. +4. If a "Bug Description" custom field was discoverable in prerequisites, write the same content to that field as raw ADF (`type: "doc"`, `version: 1`) in a separate `editJiraIssue` call. Some Jira instances reject markdown for that field type. If the field doesn't exist, skip this step silently. + +**Preserve all original media, attachments, and links.** Screenshots, videos, recordings, images, and file attachments from the original description must be carried into the refined version. Reproduce them with the same markdown image/link syntax. If the original embeds media you cannot reproduce in markdown, keep the original markup verbatim in that section. Never drop attachments, embedded images, inline links, or any referenced files. + +**Follow-up path adjustment:** when `follow_up_needed = true`, the Investigation Notes section ends with a single line naming the open question and whom it's blocked on: + +> Open question: {what you asked}. Blocked on reply from {reporter or EM name}. + +The Working Hypotheses or Root Cause section stays speculative (`[INFERRED]` / `[UNKNOWN]` tags) since we're explicitly waiting for confirmation. + +Warn the user once at the start of this phase if either fallback was used. + +--- + +### Phase 6: Severity, Due Date, or Sprint Placement + +**Applies to:** see archetype branches below. +**Skipped on:** `follow_up_needed = true` (always). + +**Bug or Incident path:** + +Read the current severity from the auto-discovered severity field. Compare it against the severity recommendation cached in Phase 2.5. + +1. **If recommendation matches current:** leave the severity field alone. Only set the due date. +2. **If recommendation differs:** update the severity field to the new option ID (looked up at runtime from the field's allowed options) in the same `editJiraIssue` call as the due date. + +Calculate the due date as `created + due_offset_days` from `severity_scheme[recommendation]` based on the severity the ticket will have after Phase 6 (new value if changed, current value otherwise). Format as `YYYY-MM-DD`. + +If the severity field is empty on the ticket, write the recommendation cached in Phase 2.5. Do not infer severity from `priority` unless `priority` is the configured severity field. + +**Feature, Task, or Spike path:** + +1. Skip the severity field and due date entirely. Severity is a Bug/Incident concept. +2. If `sprint_field_name` is configured in config: + - Look up the active sprint for the configured `project_key` (or the inferred project key from the ticket URL) via `searchJiraIssuesUsingJql` with `sprint in openSprints() AND project = ` to find a representative sprint ID. + - Set the ticket's sprint field to the active sprint ID via `editJiraIssue`. Show the user the chosen sprint name and ID before writing. +3. If `story_points_field_name` is configured AND the user provided an estimate during the Phase 3 confirmation gate, write the estimate to the configured field via `editJiraIssue`. +4. If neither field is configured, skip Phase 6 silently for non-bug archetypes. + +Skip this phase entirely when `follow_up_needed = true`. Severity, due date, sprint placement, and story points all wait until the reporter's reply comes in and the ticket is re-triaged. + +--- + +### Phase 7: Link Related Tickets + +During investigation (Phases 1-2), collect every related ticket key found in Slack threads, Jira searches, linked tickets, and comments. After the ticket is refined, link them: + +1. **Duplicates:** `createIssueLink` with `link_type: "Duplicate"`. Newer ticket = inward; canonical = outward. +2. **Related:** `createIssueLink` with `link_type: "Relates"` for tickets that cover the same area or symptom but are not exact duplicates. +3. Skip any links that already exist on the ticket (check `issuelinks` from the Phase 0 fetch). + +--- + +### Phase 8: Labels and Optional Fields + +1. Append the configured `triaged_label` (default `triaged`) to existing labels. Preserve existing ones. +2. If the "Work Type" custom field is discoverable and has an `Other` option, set it to `Other`. Skip silently otherwise. +3. Fill "Components", "Customers", "Impacted Party" if discoverable and you can determine correct values from the investigation. Leave blank otherwise. + +Use one `editJiraIssue` call when possible. + +--- + +### Phase 9: Final Update + +Apply the remaining field updates and the final transition. The field changes (assignee) go in one `editJiraIssue` call; the transition is a separate `transitionJiraIssue` call (after `getTransitionsForJiraIssue` to look up the transition ID). + +1. **Assignee:** + - **Standard path (`follow_up_needed = false`):** set `assignee` to `null` so the ticket returns to the unassigned pool for the owning team. + - **Follow-up path (`follow_up_needed = true`):** Phase 4b already assigned the ticket; do not touch the assignee in this phase. + + Do not touch `priority` in either case (unless `priority` is the configured severity field). +2. **Transition:** by archetype, severity, and path: + - **Bug/Incident standard path:** if the post-Phase-6 severity is the lowest level in `severity_scheme` (default `Sev-3`), transition to `backlog` (default `Backlog`). All other levels stay in `investigating` for the owning team to pick up promptly. No transition call is needed; the ticket is already in `investigating` from Phase 0. + - **Feature/Task/Spike standard path:** if `non_bug_transitions.ready` is configured, transition to that. Otherwise, leave the ticket in `investigating` so the owning team picks it up. + - **Follow-up path (any archetype):** transition to `waiting_reply` (default `Waiting for Reply`). Use `getTransitionsForJiraIssue` to find the transition ID. Do not send to backlog; the ticket should stay visible so the reply is seen. + +Confirm to the user what was updated, including which transition was applied and who the ticket is assigned to. + +--- + +### Phase 10: Notification + Optional Escalation + +Send a Slack DM to the running user via `slack_send_message` using the cached Slack `user_id` as `channel_id`. Never hardcode a Slack user ID. Format: + +> `: {outcome}` + +Pick the outcome that matches what you did: + +| Situation | Message | +|-----------|---------| +| Bug/Incident, lowest severity triaged | `Moved to {backlog transition} after triaging` | +| Bug/Incident, higher severity triaged | `Triaged, staying in {investigating transition} ({SevN})` | +| Feature/Task/Spike, no follow-up | `Triaged, staying in {investigating transition} ({Feature, Task, or Spike})` | +| Feature/Task/Spike, sprint placement applied | `Triaged and added to active sprint, staying in {investigating transition}` | +| Feature/Task/Spike, ready transition configured | `Triaged and moved to {non_bug_transitions.ready}` | +| Asked reporter for missing data | `Asked reporter for missing info, moved to {waiting_reply transition}` | +| Asked reporter for clarification | `Asked reporter to clarify, moved to {waiting_reply transition}` | +| Asked reporter to verify fix | `Asked reporter to confirm if still reproducing, moved to {waiting_reply transition}` | +| Asked reporter for relevance check (non-bug) | `Asked reporter if still relevant, moved to {waiting_reply transition}` | +| Asked EM (reporter deactivated) | `Reporter deactivated, asked EM {name}, moved to {waiting_reply transition}` | +| Duplicate (only if user explicitly approved closure) | `Closed as duplicate of ORIGINAL-KEY` | +| Severity changed | `Changed severity from {SevX} to {SevY}` | +| Closed (only if user explicitly approved closure) | `Closed as {resolution}` | +| Default-config first run (any archetype, any path) | (append) `Triaged with default config; run /jira-issue-triage:setup any time to customize.` | + +The "Severity changed" line should only appear when the severity field was updated. Never mention `priority` unless it was the configured severity field. Combine multiple outcomes on one line when they apply (e.g., `Changed severity from Sev-2 to Sev-3. Moved to Backlog after triaging`). + +**Escalation routing.** If the recommendation's level is marked `escalate_immediately: true` in `severity_scheme`: + +- If `escalation.slack_channel` is set, send a second message to that channel with the format shown below for Phase 10 templates, and include the cached Slack mention for `primary_contact` (resolved from the configured email at session start) if `primary_contact` is set. +- If `primary_contact` is set but `slack_channel` is not, DM the primary contact directly using the cached Slack `user_id`. +- If both are null, the running-user DM is the only escalation. The user decides what to do. +- If `primary_contact` doesn't acknowledge within the level's mitigation SLA (which the user can read from their own runbook; the agent doesn't track this) and `fallback_contact` is set, the user can ask the agent to ping the fallback. The agent does not auto-page on a timer. + +--- + +## Duplicate Detection (Phase 1 helper) + +Before completing investigation, search for potential duplicates with JQL: + +| Strategy | JQL pattern | +|----------|-------------| +| Keywords | `project = {project_key} AND summary ~ "keyword1" AND summary ~ "keyword2" ORDER BY created DESC` | +| Component | `project = {project_key} AND summary ~ "scheduling" AND status != Closed ORDER BY created DESC` | +| Error string | `project = {project_key} AND (summary ~ "TypeError" OR description ~ "Cannot read properties") ORDER BY created DESC` | + +Link confirmed duplicates with `link_type: "Duplicate"` (newer = inward, canonical = outward). Use `link_type: "Relates"` for uncertain matches. + +--- + +## Writing Rules (always active) + +These apply to all text written to the ticket, all Slack messages, and all comments. + +- Never use em dashes or spaced hyphens as separators. Restructure. +- No LLM vocabulary: delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate. +- Lead with the answer. No opener phrases. +- No trailing summaries on short sections. +- Prose over bullet lists when the content flows naturally as sentences. +- Never restate Jira-native metadata (status, priority, type, assignee) in the description. +- Never present unverified analysis as confirmed root cause. +- Never add investigation action items to the description body. diff --git a/jira-issue-triage/commands/setup.md b/jira-issue-triage/commands/setup.md new file mode 100644 index 0000000..8720519 --- /dev/null +++ b/jira-issue-triage/commands/setup.md @@ -0,0 +1,164 @@ +--- +description: First-time setup wizard for jira-issue-triage. Walks through configuration questions and writes .claude/jira-issue-triage.config.json. +argument-hint: (no args) +--- + +# jira-issue-triage Setup Wizard + +Walk the user through eight configuration questions and write the result to `.claude/jira-issue-triage.config.json`. Re-runnable: pointing the wizard at an existing config offers to overwrite or keep current. + +## Steps + +### 1. Check for existing config + +Read `.claude/jira-issue-triage.config.json` if it exists. Also check `.claude/jira-bug-triage.config.json` (the legacy path used by the v0.3.0 plugin). + +- **New-path config exists:** Read it, show the current contents to the user as pretty-printed JSON, then ask via `AskQuestion`: "Overwrite the existing config?" Options: `Yes, walk through the wizard again`, `No, keep current and exit`. On "No", exit cleanly. +- **Only the legacy file exists:** Read it, show the contents, and tell the user: "The legacy file works for now but the new path `.claude/jira-issue-triage.config.json` is preferred. The wizard will write the new path; you can delete the legacy file once the new one is written." Continue to step 2. +- **Neither exists:** Continue to step 2. + +### 2. Auto-discover defaults + +Best-effort auto-discovery to suggest defaults. Failures are non-fatal; fall back to the static defaults listed in each question and tell the user the auto-discovery failed. + +1. Call `getAccessibleAtlassianResources` to get the cloudId and the list of accessible Atlassian sites. +2. Call `atlassianUserInfo` to get the user's account info. +3. From the user's accessible sites, pick the first site as the suggested Atlassian/Jira context for later operations. The two calls above do not return a Jira project list, so the wizard does not auto-detect a project key. Keep the Q1 default as `infer` and only switch to a real key if the user types one in. +4. Pre-populate the severity field name auto-discovery order: `Severity Level` -> `Severity` -> `Bug Severity` -> `priority`. The wizard surfaces these as Q2 options. + +### 3. Walk through the eight wizard questions + +Ask one at a time. Use `AskQuestion` for multiple-choice answers. Use plain free-text prompts for names, emails, labels, and channel names. Confirm each answer before moving to the next question. + +#### Q1: Default project key + +Free-text prompt: + +> Default Jira project key? Type a key like `ENG` or `BUG`, or type `infer` to derive it from each ticket URL. + +Default: `infer`. Validate that the answer is either `infer` or a non-empty alphanumeric string (uppercase Jira keys allowed). + +**Serialization rule.** `infer` is a UI sentinel, not a saved value. When the user answers `infer` (or accepts the default), write `"project_key": null` in the saved JSON config. The agent's Phase 0 inspects `project_key` and infers from each ticket URL when the value is `null`; saving the literal string `"infer"` would be treated as a real Jira project key in JQL and break ticket lookups. When the user types a real key, save it as a JSON string (e.g., `"project_key": "ENG"`). + +#### Q2: Severity field name + +Use `AskQuestion`: + +> Which Jira field holds the severity for bug tickets? + +Options: +- `Severity Level` (recommended default for many Jira instances) +- `Severity` +- `Bug Severity` +- `priority` (use the native Jira priority field as severity) +- `Custom (type the name)` + +On "Custom", ask for the field name as free text. Validate the answer is non-empty. + +#### Q3: Triaged label + +Free-text prompt: + +> Label to add to tickets after triage? Default: `triaged`. + +Default: `triaged`. + +#### Q4: Skip labels + +Free-text prompt: + +> Comma-separated list of label prefixes that should skip triage entirely (e.g., `applause,external-vendor`). Press Enter for none. + +Default: empty list. Parse the answer by splitting on `,` and trimming whitespace; reject any entry containing whitespace inside the value (warn and re-prompt). + +**Serialization rule.** Save as a JSON array of strings, even when empty. An empty answer (Enter pressed) writes `"skip_labels": []`, not `null` and not `""`. + +#### Q5: Transition names + +Three free-text prompts in sequence: + +1. > Transition name for "investigating"? Default: `Under Investigation`. +2. > Transition name for "waiting for reply"? Default: `Waiting for Reply`. +3. > Transition name for "backlog"? Default: `Backlog`. + +#### Q6: Severity scheme + +Use `AskQuestion`: + +> Which severity scheme do you want to use? + +Options: +- `3-tier (Sev-1, Sev-2, Sev-3) with 7/14/90 day SLAs` (recommended default) +- `5-tier (Sev-1, Sev-1.5, Sev-2, Sev-2.5, Sev-3)` +- `Custom (specify each level)` + +On "5-tier", use the static 5-tier scheme that strictly extends the 3-tier defaults so users do not get surprise SLA changes when they switch tiers: `Sev-1` (7 days, escalate), `Sev-1.5` (7 days, escalate), `Sev-2` (14 days), `Sev-2.5` (30 days), `Sev-3` (90 days). This matches the 5-tier example in the plugin README. + +On "Custom", walk through each level: ask for the level name, the `due_offset_days` integer, and via `AskQuestion` whether `escalate_immediately` is `Yes` or `No`. Loop until the user types `done` for the level name. + +#### Q7: Escalation + +Three free-text sub-prompts. Each accepts an empty answer (Enter for none). + +1. > Slack channel for high-severity escalation pings? (e.g., `#bug-triage`) Press Enter for none. +2. > Primary escalation contact? Format: `Alice Kumar `. Press Enter for none. +3. > Fallback escalation contact? Same format. Press Enter for none. + +Parse the contact strings into `{ "name": "Alice Kumar", "email": "alice@example.com" }`. If the format does not match, warn and re-prompt. + +**Serialization rule.** Empty answers map to JSON `null`, not empty strings or empty objects. Specifically: an empty Slack channel writes `"slack_channel": null`; an empty primary contact writes `"primary_contact": null`; an empty fallback contact writes `"fallback_contact": null`. The agent treats `null` on any of these three fields as "no escalation configured for this slot". + +#### Q8: Save? + +Show the assembled config as pretty-printed JSON with sorted top-level keys. Use `AskQuestion`: + +> Save this config to `.claude/jira-issue-triage.config.json`? + +Options: +- `Yes, write the file` +- `No, discard and exit` +- `Edit a specific question (which one?)` + +On `Edit`, ask which question number to revisit, re-prompt that question, and loop back to Q8. + +### 4. Write the config file + +Use the `Write` tool with `path: ".claude/jira-issue-triage.config.json"`. Pretty-print with two-space indent and sort top-level keys alphabetically for stable diffs. The full schema (with all top-level keys, in alphabetical order): + +```json +{ + "escalation": { "slack_channel": null, "primary_contact": null, "fallback_contact": null }, + "non_bug_transitions": { "ready": null }, + "project_key": null, + "scope_summary_field_name": null, + "severity_field_name": null, + "severity_scheme": { + "Sev-1": { "due_offset_days": 7, "escalate_immediately": true }, + "Sev-2": { "due_offset_days": 14, "escalate_immediately": false }, + "Sev-3": { "due_offset_days": 90, "escalate_immediately": false } + }, + "skip_labels": [], + "sprint_field_name": null, + "story_points_field_name": null, + "transitions": { + "investigating": "Under Investigation", + "waiting_reply": "Waiting for Reply", + "backlog": "Backlog" + }, + "triaged_label": "triaged" +} +``` + +The four trailing optional fields (`scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`) are NOT asked in this wizard. They default to null/empty in the written config and can be added by editing the file directly. See the plugin README for documentation. + +### 5. Confirmation message + +Print one line: + +> Wrote `.claude/jira-issue-triage.config.json`. You can re-run `/jira-issue-triage:setup` any time to update. + +## Notes + +- This wizard never modifies Jira. Read-only auto-discovery only. +- If `getAccessibleAtlassianResources` or `atlassianUserInfo` fails, proceed with the static defaults and tell the user the auto-discovery failed. +- The wizard does not validate the entered Jira field names against the live instance. The agent's auto-discovery (in Prerequisites) handles validation at runtime; if a configured field name does not resolve, the agent falls back to its built-in auto-discovery order and warns the user. diff --git a/jira-issue-triage/skills/issue-investigator/SKILL.md b/jira-issue-triage/skills/issue-investigator/SKILL.md new file mode 100644 index 0000000..71a1dde --- /dev/null +++ b/jira-issue-triage/skills/issue-investigator/SKILL.md @@ -0,0 +1,197 @@ +--- +name: issue-investigator +description: "Investigates a Jira Bug or Incident ticket by searching Slack, the ticket and related Jira/Confluence pages, Datadog, and the codebase, then writes an evidence-tagged report in the bug-archetype template. Use when a Bug or Incident ticket needs an investigation report before triage decisions are made. For Feature, Task, or Spike tickets, see `requirements-investigator`." +metadata: + author: Taha Bikanerwala +tools: Read, Bash, Grep, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql, mcp__plugin_atlassian_atlassian__searchConfluenceUsingCql, mcp__plugin_slack_slack__slack_search_public_and_private, mcp__plugin_slack_slack__slack_read_thread, mcp__datadog__search_datadog_logs +--- + +# Issue Investigator + +Produce a structured report that orients an engineer for a Jira bug ticket. The report names what is broken, ranks 2-3 hypotheses, lists concrete next-step queries, and tags every claim with its evidence level. + +**Scope:** Bug and Incident archetypes. For Feature, Task, or Spike tickets, the `jira-issue-triage` agent calls `requirements-investigator` instead. + +This skill investigates. It does not solve, post, or modify anything. + +## Calling Convention + +This skill runs without user interaction. The constraints below let it work cleanly inside the `jira-issue-triage` agent (which has its own confirmation gate) and standalone. + +- **Non-interactive.** Never ask the user a question. Inputs are inferred from the ticket and search results. +- **Predictable structure.** Same six section headers every run, in the same order, with one allowed reorder for production incidents (see Adaptation Rules). +- **Same evidence tags.** Always `[VERIFIED]`, `[OBSERVED]`, `[INFERRED]`, `[UNKNOWN]`. +- **Output is the last thing.** Skill ends after the report renders. No follow-up prompts. +- **Read-only.** No `editJiraIssue`, no `addCommentToJiraIssue`, no `slack_send_message`. Posting is the caller's job. + +## Search Ladder + +Investigation runs four levels top to bottom. Each level has a gate: if it produces enough evidence to write a useful report, skip the remaining levels. + +### Setup + +Before running the levels, fetch the ticket data once and cache it for the rest of the skill. + +1. Identify the ticket key from the invocation context (e.g., `BUG-12345` from a pasted URL or a parameter passed by the caller). If the calling context (such as the `jira-issue-triage` agent) has already fetched the ticket and exposed the payload, reuse that payload; don't fetch again. +2. If no payload is available, call `getJiraIssue` with the ticket key and `responseContentFormat: "markdown"`. Request these fields at minimum: `summary`, `description`, `status`, `priority`, `labels`, `components`, `assignee`, `reporter`, `created`, `updated`, `issuelinks`. +3. Cache the response. Reference it as "the ticket payload" throughout the skill — `summary`, `description`, `created`, `issuelinks`, `reporter`, etc. + +If `getJiraIssue` fails (auth error, ticket not found, network), stop and tell the caller which call failed. Do not proceed without ticket data. + +### Level 1: Slack + +Run 2-3 queries via `slack_search_public_and_private`: + +1. The ticket key (e.g., `BUG-12345`). +2. The most distinctive symptom or error message. +3. The customer or area name combined with a key term. + +For each relevant hit, follow the thread in full with `slack_read_thread`. + +What you are looking for: +- An engineer who already identified the root cause. +- A workaround that was shared. +- A specific service, config setting, or deploy named as the culprit. +- Links to relevant PRs, commits, or Jira tickets. + +**Gate:** if a Slack thread contains a confirmed root cause or workaround, write the report citing that thread and skip Levels 2-4. + +### Level 2: Ticket + Jira + Confluence + +Read the ticket payload (cached in Setup) carefully. Signals are easy to miss on a fast scan: error messages, timestamps, customer names, browser/device, the question the reporter is actually asking. + +Then search: + +- **Jira related tickets** via `searchJiraIssuesUsingJql`. Common patterns: + - `project = "" AND text ~ "" ORDER BY created DESC` + - `project = "" AND summary ~ "" AND status != Closed ORDER BY created DESC` + - `project = "" AND component = "" ORDER BY created DESC` +- **Linked tickets.** Follow every `issuelinks` entry from the original `getJiraIssue` payload. +- **Confluence** via `searchConfluenceUsingCql`. Look for runbooks, architecture pages, known-issues pages, onboarding docs. Use the feature area, system name, or entity type as the search term. + +For each related Jira ticket, record: key, summary, status, assignee, the most relevant finding from description or comments. +For each Confluence page, record: URL and a 1-line summary. + +**Gate:** if a runbook describes the exact scenario or a prior Jira ticket has the resolution, write the report pointing at that source. Skip Levels 3-4. + +### Level 3: Datadog + +Build queries from signals collected in Levels 1-2: error strings, service names, entity IDs, HTTP status codes. + +Call `search_datadog_logs` with: +- `query`: e.g., `service:my-service status:error @http.status_code:500 @user_id:abc123` +- `from`: 7 days before the ticket's `created` date, or the timeframe mentioned in the ticket +- `to`: ticket `created` date or now +- `limit`: 10-25 + +Build a Logs URL the engineer can click: +`https://app.datadoghq.com/logs?query=&from_ts=&to_ts=` + +**Suppression rule:** if Datadog returns any error (auth, 403/404, timeout, rate limit, empty results, or any non-success), treat Datadog as unavailable for this ticket. Do not mention Datadog anywhere in the report. This rule overrides every other instruction that references Datadog data. + +**Gate:** if Datadog returned usable results that identify a service, an error pattern, or a timeline gap, write the report incorporating those findings. Skip Level 4 unless an external source points specifically to a code-level cause. + +### Level 4: Code + +Enter only when Levels 1-3 turned up nothing useful, OR external sources point to a code-level cause that needs tracing. + +1. **Error strings.** Use `Bash` (e.g., `grep -r 'pattern' path/`) or `Grep` to find error messages in the codebase. Identify which service owns the error. +2. **Endpoints or event handler names.** Search for route definitions or event handler names to confirm which service handles the affected flow. +3. **Observable signals.** Use `Read` to open source files near the relevant code; find logging and monitoring calls. For each call found, note the log message string and any structured tags so the "Where To Look" section can name them. +4. **Recent changes.** Run `git log --since="2 weeks ago" -- ` via `Bash` to find commits that correlate with the reported timeline. + +Stop when you can name: which service is involved, what signals are observable, and 2-3 concrete observability queries. Do not trace full call chains unless the chain itself is the finding. + +## Evidence Model + +Every claim in the report carries one of four tags. + +| Tag | Meaning | +|-----|---------| +| `[VERIFIED]` | Directly confirmed. Read in code, or a source explicitly states this. | +| `[OBSERVED]` | A pattern matches the reported behavior, but reaching the conclusion required a logical step. | +| `[INFERRED]` | Logical deduction from available information. Not directly observed. | +| `[UNKNOWN]` | Cannot determine from available sources. Requires runtime data. | + +If the finished report has more `[INFERRED]` than `[VERIFIED]` findings, the search was insufficient. Go back and search more before writing. + +Every `[UNKNOWN]` becomes a "Where To Look" item: name the runtime check that would resolve it. + +## Stop Condition + +Investigation is **done** when all three are true: + +1. There are 2-3 ranked hypotheses, most-likely first. **Exception:** if the Level 1 or Level 2 gate fired with a confirmed root cause, a single hypothesis is sufficient. +2. At least one source has been consulted at every search level the investigation reached. (If Level 1 closed via its gate, Levels 2-4 do not need sources.) +3. There are concrete next-step queries or files in "Where To Look". + +If any one is missing, keep investigating. + +## Report Template + +Every report has all six sections. If a section has nothing meaningful to say, write a 1-line note ("Not applicable for this ticket") rather than skip the section. + +### 1. Lead + +1-2 sentences. Name what is broken and your single best hypothesis. Inline evidence tag. Do not restate the ticket title. + +Example: + +> Sessions for tenant `MapleTower` started failing at the join step yesterday after deploy `2026-04-29T18:00Z`; the new SSO middleware is the most likely cause `[OBSERVED]`. + +### 2. Scope & Status + +Who is affected (one user, a segment, or all). Whether investigation is complete or needs runtime verification. Stale-ticket flag if the ticket has been quiet for more than 2 weeks while the bug may already be fixed. + +### 3. Domain Context + +2-4 sentences. Define vendor names, internal acronyms, or product terminology a new team member would not know. Skip with "Not applicable" if the affected area is obvious from the title. + +### 4. What Happened + +2-4 sentences. Plain language. Include the exact error message and when the issue started if known. + +### 5. What We Found + +Narrative prose with evidence tags inline. Cover: + +- Which service or component owns the behavior. +- 2-3 hypotheses ranked by likelihood, each with its evidence trail. +- Recent changes (deploys, PRs, config) that correlate with the timeline. +- Related prior tickets and what they say. + +No tables in this section. No code snippets unless the snippet itself is the finding (then keep it short). + +### 6. Where To Look + +2-5 tool-by-tool items. Each item: + +- Names the tool (code search, Slack search, admin URL, Sentry, Datadog, etc.). The list reflects tools the engineer should use after reading the report, not tools this skill itself queried. +- Gives the exact ready-to-paste query, URL, or file path. +- Says in one phrase what a hit or miss tells you. +- Datadog items appear here only if Datadog returned usable results during Level 3. The Level 3 suppression rule overrides this whenever Datadog was unavailable. + +Example: + +> - **Code search:** `grep -r 'SSO_TOKEN_EXPIRED' services/auth/` to find the error string in source. A hit identifies the service that owns the failure mode; a miss means the error originates outside the auth service. + +## Adaptation Rules + +These rules adjust section order or content emphasis. All six sections still appear every run. + +- **Found at Level 1 (Slack):** Section 5 leads with the Slack source and links the thread. Sections 3, 4 may be 1 line each. +- **Found at Level 2 (runbook or prior Jira ticket):** Section 5 leads with the source. Same brevity allowed elsewhere. +- **Required Levels 3-4 (code/logs):** Section 5 includes code references inline as `path/to/file.ext:line`. No long code snippets unless the snippet is the finding. +- **Production incident (live impact):** Reorder. Put Section 6 ("Where To Look") immediately after Section 1 ("Lead"). Sections 2-5 follow. Engineers reading this need next actions before context. +- **Vague ticket (almost no signal):** Section 5 describes what was searched and what is unknown. Section 6 ends with a single `Where To Look` item naming the specific information the reporter could provide, phrased as a concrete question for the owning team to use if they choose to contact the reporter. The skill itself never contacts the reporter. + +## Writing Rules + +These apply to all text in the report. + +- No em dashes or spaced hyphens as separators. Em dashes inside parenthetical asides are fine. +- No LLM vocabulary: delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate. +- Lead with the answer. No opener phrases. +- No trailing summaries on short sections. +- Prose over bullet lists when the content flows naturally as sentences. +- Never present unverified analysis as a confirmed root cause. diff --git a/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md b/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md new file mode 100644 index 0000000..4528f05 --- /dev/null +++ b/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md @@ -0,0 +1,200 @@ +--- +name: jira-ticket-refiner +description: "Restructures a poorly written Jira ticket into a clear, self-contained document that a stranger can read cold and act on. Works on any ticket type (bug, feature, task, spike, incident). Updates the description and title via the Atlassian MCP and never deletes original content. Use when the user asks to refine, rewrite, restructure, clean up, or improve a Jira ticket." +metadata: + author: Taha Bikanerwala +tools: Read, mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__editJiraIssue, mcp__plugin_atlassian_atlassian__addCommentToJiraIssue +--- + +# Jira Ticket Refiner + +Take a Jira ticket that is hard to read and turn it into a document a stranger can open cold, in a year, and act on. Reorganize the content. Never delete it. Every fact in the original survives the rewrite, just placed somewhere it can be found. + +This skill modifies Jira. It updates the ticket's description and title (the `fields.summary` API field) via the Atlassian MCP, and posts an optional next-steps comment when the user asks for one. + +## Calling Convention + +The skill works two ways. When a user pastes a ticket key and asks to refine it, run end to end. When the `jira-issue-triage` agent calls this skill in Phase 5, treat the agent's already-fetched payload and investigation findings as the source data and skip the fetch step. + +- **One confirmation gate.** Always preview the rewrite before calling `editJiraIssue`. The user must approve. +- **Read-then-write.** Refuse to write before reading the description, comments, and (when relevant) changelog. +- **Strict superset.** The refined ticket contains every fact from the original. Restructure, rewrite, and re-tag, never truncate. +- **No solution prescription.** The skill structures information. It does not invent fixes, recommend roadmap, or editorialize on causes that are not in the ticket. + +## Prerequisites + +- The user provides a Jira ticket key (e.g., `BUG-1234`) or a ticket URL. +- The Atlassian MCP server is configured. `getAccessibleAtlassianResources` returns at least one site so the `cloudId` is resolvable. +- The user has edit permission on the ticket. + +If any prerequisite fails, stop and tell the user which call failed before continuing. + +## Workflow + +The workflow is eight ordered steps. Steps 1, 5, and 6 require reading the corresponding reference file when you reach that step. Do not pre-load the references at the start of the run. + +1. **Fetch** the ticket: fields, comments, changelog when needed, and linked tickets. +2. **Classify** the ticket archetype (Bug / Feature / Task / Incident / Spike). The archetype controls which sections appear in the refined description. +3. **Inventory** every distinct fact across the description, comments, changelog, and linked tickets. Tag each fact with its information category. +4. **Rewrite** the inventory into prose, applying the rewrite principles. +5. **Apply the template** in `assets/template.md`, including only the sections the archetype calls for. +6. **Rewrite the title** so a reader on a board view can decide whether to open the ticket. +7. **Preview** the proposed title and description as inline markdown. Wait for the user to approve. +8. **Post a next-steps comment** only when the user asks for one. + +--- + +### Step 1: Fetch the Ticket + +Read [references/gathering-guide.md](references/gathering-guide.md) when you reach this step. It documents which fields to request, the ADF content-loss check, and the completeness gate that blocks Step 2 until comments and changelog are read. + +If the calling context (the `jira-issue-triage` agent in Phase 5) has already fetched the ticket and exposes the payload, reuse it. Do not refetch. + +### Step 2: Classify the Archetype + +Use the table below. The archetype is the single most important variable in the rewrite because it picks which template sections appear in the final description. + +| Archetype | Typical Jira issue types | What the content looks like | +|-----------|--------------------------|------------------------------| +| **Bug** | Bug, Defect | A user-visible symptom, an error, broken behavior, or unexpected output. May include reproduction steps. | +| **Feature** | Story, Feature, Enhancement, New Feature | A user need or business goal, acceptance criteria, design specs, links to product briefs. | +| **Task** | Task, Sub-task, Chore, Tech Debt | Operational work: cleanup, configuration, migration, dependency upgrade, runbook execution. | +| **Incident** | Incident, Outage, SEV-tagged tickets | Production impact, blast radius, timeline, mitigation steps, post-mortem context. | +| **Spike** | Spike, Research, Investigation | Open questions to resolve, exploration scope, proof-of-concept boundaries, preliminary findings. | + +**When the issue type and the content disagree, trust the content.** A ticket typed `Bug` whose body is acceptance criteria and a Figma link is a Feature. A ticket typed `Task` describing user-visible breakage is a Bug. The content drives the template, not the Jira issue type field. + +Hold the archetype as working context. Do not surface it in the preview unless the user asks. + +### Step 3: Inventory the Information + +Catalog every distinct fact found in the description, every comment, the changelog (if read), and any linked tickets that add scope. Read [references/classification-guide.md](references/classification-guide.md) when you reach this step. It defines the seven information categories and the verified-vs-unverified flag every item carries. + +### Step 4: Rewrite + +Apply the rewrite principles from `references/classification-guide.md` (already loaded in Step 3). The principles cover symptoms-over-solutions, evidence-over-claims, prose-over-tables, and the rules for surfacing decisions buried in comment threads. + +### Step 5: Apply the Template + +Structure the rewritten content using `assets/template.md`. Read it now, alongside [references/jira-formatting.md](references/jira-formatting.md) for the markdown-to-ADF safety rules. + +Three rules are non-negotiable: + +- **Pick sections by archetype.** The template ships with an archetype-to-sections map. Include only the sections that map says belong to the current archetype. +- **Skip empty sections.** A section header with no content under it is noise. Omit it. +- **Fold raw metadata into the body.** Source-system blocks (intake forms, support escalation tables, Zendesk dumps, Applause exports) get their facts extracted and placed in the appropriate template section. Do not preserve the raw block verbatim. The only time an Original Metadata section is appropriate is when a fact is genuinely unclassifiable and has no other home. + +### Step 6: Rewrite the Title + +Read [references/title-guide.md](references/title-guide.md) when you reach this step. It documents the title pattern, the archetype-by-archetype examples, and the character budget. + +### Step 7: Preview and Confirm + +Preview before posting. The preview is the user's only chance to catch a mistake before the ticket is overwritten. + +**Render the preview as inline markdown.** The refined description contains its own fenced code blocks (errors, queries, JSON). Wrapping the whole preview in an outer code fence breaks every inner block. Use this exact layout: + +1. A horizontal rule. +2. The new title on its own line, formatted as bold `Title:` followed by the rewritten title text inside an inline-code span. "Title" here means the user-facing label and the value that will land in the Jira `fields.summary` field. The literal markdown to emit is shown below. +3. A blank line. +4. The full refined description rendered as plain markdown, no outer fence. +5. A horizontal rule. +6. A short prompt asking the user to approve or request changes. + +The title line in step 2 looks like this when emitted as markdown: + +```markdown +**Title:** `{the rewritten title}` +``` + +Do not pad the preview with workflow notes (`Archetype:`, `ADF warning:`, `Previous state:`). The preview is what will appear on the ticket. If the rewrite carries a real risk of losing ADF-only content (panels with substantive text, mentions, task lists with checked items, expand sections, embedded media, complex tables), say so in one sentence outside the preview. Do not warn for cosmetic-only losses such as a smart-link card becoming a plain URL. + +After the user reviews: + +- **Approved.** Call `editJiraIssue` once with `fields.summary` and `fields.description` set. Use `contentFormat: "markdown"` for the description. Never send raw ADF JSON in the description field. +- **Changes requested.** Revise and re-preview. Do not call `editJiraIssue` until the user explicitly approves. + +### Step 8: Post a Next-Steps Comment + +Skip this step unless the user asks for it. + +When asked, post via `addCommentToJiraIssue` with `contentFormat: "adf"`. The comment body is a JSON-stringified ADF (Atlassian Document Format) doc. Never use `contentFormat: "markdown"` for comments in this plugin: markdown escapes mention brackets, link targets, and rich marks, which silently breaks notifications and renders chips as literal text. The same rule appears in the `jira-issue-triage` agent body so the plugin stays consistent. + +Build the ADF doc with these nodes: + +- A `heading` node (`attrs.level: 2`) whose text is `Next Steps (YYYY-MM-DD)`. Substitute today's date in `YYYY-MM-DD` form. Use parentheses for the date so the heading does not need a separator. +- An `orderedList` node containing one `listItem` per action. Each `listItem` wraps a `paragraph` whose `content` is one or more `text` nodes. +- Every action names an owner or team (as plain text, not a `mention` node, unless the user explicitly approved tagging that account). +- Use concrete verbs. Write `Verify token rotation schedule with Platform team` rather than `Look into auth`. + +The skeleton below is the structure to build, shown as a JSON object for readability. Before passing it to `addCommentToJiraIssue`, serialize it with `JSON.stringify(adfDoc)` and put the resulting string in `commentBody`. The `YYYY-MM-DD` token is a placeholder; substitute the real date. + +```json +{ + "type": "doc", + "version": 1, + "content": [ + { + "type": "heading", + "attrs": { "level": 2 }, + "content": [{ "type": "text", "text": "Next Steps (YYYY-MM-DD)" }] + }, + { + "type": "orderedList", + "content": [ + { + "type": "listItem", + "content": [ + { + "type": "paragraph", + "content": [ + { "type": "text", "text": "Verify token rotation schedule with Platform team." } + ] + } + ] + } + ] + } + ] +} +``` + +The full call shape (with the structure above stringified into `commentBody`): + +```json +{ + "contentFormat": "adf", + "commentBody": "{\"type\":\"doc\",\"version\":1,\"content\":[ ... ]}" +} +``` + +Comment body actions belong here, not in the description. The description records what is known and unknown. The comment records what happens next. + +--- + +## Anti-Patterns + +These are hard rules. Each one prevents a failure mode that has been observed on real refinements. + +1. **Never present unverified analysis as confirmed root cause.** Frame it as a working hypothesis in the Working Hypotheses section, in plain prose. No disclaimer block, no warning panel. +2. **Never inject solutions that are not in the original ticket.** If the original contains evidence pointing toward a fix, surface the evidence. Drop the solution. +3. **Never add roadmap or tech-debt suggestions.** A ticket description is a record. It is not a forum for "we should also...". +4. **Never replace the description with less information.** The refined version is a strict superset of the original. The amount of unique information cannot decrease. +5. **Never lose investigation artifacts.** Customer IDs, log links, query results, error strings, attachment URLs, screenshot embeds, video links: every one survives. +6. **Never use escaped newlines.** `\n` renders as a literal backslash-n in Jira. Use real line breaks. +7. **Never send raw ADF JSON in the description field.** The MCP server runs markdown-to-ADF conversion every time. A raw ADF object fails with `Failed to convert markdown to adf`. +8. **Never restate sidebar metadata.** Status, priority, issue type, parent epic, assignee, reporter, labels, and components are all visible in the Jira UI. Repeating them in the body adds noise. The exception is comment-thread context, which is hidden behind a tab and worth surfacing. +9. **Never use bug sections on non-bug tickets.** Features have no reproduction steps. Tasks have no expected/actual behavior. Spikes have no acceptance criteria. The archetype-to-sections map exists for this reason. +10. **Never put a to-do list in the description.** Queries to run, dashboards to check, flags to verify: those are next-step actions. They belong in a comment (Step 8) when the user asks for one. Frame open questions in the description as named open questions, not as a checklist. + +## Writing Style + +These rules apply to everything this skill produces: ticket descriptions, summaries, preview text, comments, and any prose addressed to the user. + +- **No em dashes or spaced hyphens as separators.** Restructure the sentence. Em dashes inside parenthetical asides are fine. +- **No LLM vocabulary.** Strike: delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate. +- **Lead with the answer.** No opener phrases (`In this ticket, we need to...`). The first sentence states what the ticket is about. +- **No trailing summary on a short section.** If the section is three sentences, the third sentence is not a recap of the first two. +- **Prefer prose over bullets** when the content reads cleanly as sentences. Bullets are for genuinely list-shaped data. + +If the user has the `prose-style` skill installed, defer to it after this skill produces the rewrite. The rules above are the floor. diff --git a/jira-issue-triage/skills/jira-ticket-refiner/assets/template.md b/jira-issue-triage/skills/jira-ticket-refiner/assets/template.md new file mode 100644 index 0000000..cdecd9a --- /dev/null +++ b/jira-issue-triage/skills/jira-ticket-refiner/assets/template.md @@ -0,0 +1,198 @@ +# Refined Ticket Description Template + +Read this when you reach Step 5 (Apply the Template). The template is a menu, not a form. Pick the sections the archetype calls for. Skip the rest. Never include an empty header. + +Write each section as readable prose where the prose flows naturally. Use a list or table only when the data really is list- or table-shaped. + +## Archetype-to-Sections Map + +The first column lists every section the template defines. The next five columns indicate whether each section appears for a given archetype. + +| Section | Bug | Feature | Task | Incident | Spike | +|---------|:---:|:-------:|:----:|:--------:|:-----:| +| Summary | always | always | always | always | always | +| Context and Background | if present | always | always | if present | always | +| Impact | if present | if present | skip | always | skip | +| Affected Scope | if present | if present | skip | always | skip | +| Reproduction, Expected, Actual | always | skip | skip | if present | skip | +| Requirements and Acceptance Criteria | skip | always | always | skip | skip | +| Questions to Answer | skip | skip | skip | skip | always | +| Findings | skip | skip | skip | skip | if present | +| Investigation Notes | if present | skip | skip | if present | if present | +| Working Hypotheses | if present | skip | skip | if present | skip | +| Root Cause | if present | skip | skip | if present | skip | +| Workaround | if present | skip | skip | if present | skip | +| Solutions | if present | skip | if present | if present | skip | +| Open Blockers | if present | if present | if present | if present | skip | +| Original Metadata | last resort | last resort | last resort | last resort | last resort | + +**Key:** `always` includes regardless of content; `if present` includes only when the original ticket has relevant material; `skip` never includes; `last resort` includes only when truly unclassifiable content has no other home. + +--- + +## Section Definitions + +Each section below shows the heading, a one-paragraph description of what belongs there, and (where useful) a small structural template. The actual headings in the refined description should match exactly. + +### Summary + +One to three sentences. The anchor of the ticket. A reader who only reads this section should know what the ticket is about and roughly why it matters. + +- **Bug:** lead with the symptom and who is affected. `Visitor notifications stop sending for scheduled visits at MapleTower properties starting 2026-04-29.` Do not lead with a proposed cause. +- **Feature:** lead with the user need or business goal. `Property managers need a bulk-invite flow so they can onboard residents in batches during move-in events.` +- **Task:** lead with what needs to happen and the why-now. `Migrate the Jest configuration from v28 to v30 so the upgrade unblocks the testing-library v15 bump scheduled for next quarter.` +- **Incident:** lead with what is broken and the blast radius. `Payment processing is failing for all US-region properties since 14:32 UTC. Customers cannot pay rent or fees.` +- **Spike:** lead with the question or uncertainty. `Determine whether AWS Cognito or Auth0 better fits multi-tenant SAML requirements before the Q3 SSO project starts.` + +### Context and Background + +Why this ticket exists. Prior decisions, related history, links to product briefs, design docs, runbooks, related Jira tickets, or Slack threads. Especially important on Features (link the brief and design doc) and Tasks (explain why-now). Skip this section when the Summary is already self-sufficient. + +### Impact + +Who is affected and how badly. Placed immediately after Summary because it controls how a triager prioritizes the ticket. + +- **Incident:** number of affected users or tenants, business impact (revenue, SLA, regulatory), timeline (when it started, when detected, current duration). +- **Bug:** user-facing consequences, scope of affected population, business or workflow implications. +- **Feature:** the business or user impact of doing the work, plus the cost of not doing it. + +### Affected Scope + +Who or what is affected and how broadly. One to three items as prose; four or more as a table. + +Prose example: + +> Affects all Bread Financial members whose cohort has `offshore_support_enabled` set to `false`. Roughly 12,000 active accounts as of 2026-04-29. + +Table example (four or more parties): + +| Affected party | What they experience | Identifiers | +|----------------|----------------------|-------------| +| MapleTower residents | No visitor notifications | `tenant_id: mt-01`, `region: us-east-1` | +| ParkPlace residents | No visitor notifications | `tenant_id: pp-04`, `region: us-east-1` | + +### Reproduction, Expected, Actual + +Bug-only. Three top-level sections that always travel together. Use these exact headings, in this order, with no shared parent heading above them: + +``` +## Reproduction Steps +1. Step 1 +2. Step 2 +3. Step 3 + +## Expected Behavior +What should happen. + +## Actual Behavior +What does happen. Include the exact error message verbatim, in a fenced code block when it has formatting. +``` + +The Section Order list below treats the trio as a single slot (position 5) because they only make sense as a unit, not because they share a parent heading. + +When the bug is not reproducible on demand, replace the steps with: `Not reproducible on demand. The reporter saw it once at {timestamp}; subsequent attempts at {follow-up timestamps} did not reproduce.` + +### Requirements and Acceptance Criteria + +Features, Stories, and Tasks. Each criterion testable. For Tasks, frame as definition of done. + +``` +1. Bulk invite endpoint accepts up to 500 emails per request. +2. Returns 207 Multi-Status with per-email success or failure when any subset fails. +3. Audit log records the inviting user and timestamp. +4. Rate limit of 10 requests per minute per property. +``` + +### Questions to Answer + +Spike-only. Each question must produce a definitive answer. Vague questions ("Is X better?") are split into specific ones ("Does X support feature Y under conditions Z?"). + +``` +1. Does AWS Cognito support SAML federation for more than 100 identity providers? +2. What is the per-tenant configuration overhead for Cognito vs Auth0? +3. Does either provider expose a usable audit log for compliance reporting? +``` + +### Findings + +Spike-only. Answers map one-to-one to the questions above, each with supporting evidence. Skip if the spike has not started. + +### Investigation Notes + +Artifacts from completed investigation. Include only when there is something concrete to record. Future investigation steps belong in a next-steps comment, not here. + +The outer fence below uses four backticks so the inner code block (the captured stack trace) can stay as a plain three-backtick fence with no escaping. When you copy this into a refined ticket, drop the outer fence entirely; it exists here only because the example wraps a sample section in this template document. + +```` +**Observability** +- [Datadog: 500s on /payments endpoint, 14:00 to 16:00 UTC](https://app.datadoghq.com/logs?query=...) + +**Errors** + +``` +Stripe::APIConnectionError: Connection refused (errno: ECONNREFUSED) + at lib/stripe/client.rb:88 +``` + +**Relevant IDs** +- Tenant: `mt-01` +- Customer: `cus_O12abc34` +- Failing payment intent: `pi_3OabcDEF456` +```` + +### Working Hypotheses + +Unverified analysis. Plain prose, no disclaimer block, no warning panel. Frame each hypothesis as the most likely cause based on available evidence, name the evidence, and name the runtime check that would confirm or rule it out. + +> The most likely cause based on the timeline is the auth-token rotation that landed in the 14:00 deploy. The rotation logic does not pre-warm token caches, so the first wave of requests after deploy hits a token-fetch path that has not been load-tested. Confirming this requires a Datadog query against `service:auth status:error` for the 14:00 to 14:15 window. + +Skip when there are no hypotheses or when the cause is verified (in which case use Root Cause). + +### Root Cause + +The cause is verified with evidence. Plain prose. Cite the evidence. If the cause is still being verified, use Working Hypotheses instead. Bugs and Incidents only. + +### Workaround + +Steps an affected user can take right now to mitigate. If there is no workaround, write `None.` Do not omit this section for an open Bug or Incident with a confirmed Root Cause; an empty workaround answer is itself useful information. + +### Solutions + +The permanent fix or resolution path. For resolved tickets, what was done. For open tickets, what a complete fix would look like, when known. Bugs, Tasks, and Incidents. + +### Open Blockers + +External parties or cross-team coordination required to close the ticket. One to two as prose; three or more as a table. + +| Blocker | Owner | Status | +|---------|-------|--------| +| Awaiting security review | Security team | Pending | +| Need new IAM role created | Platform team | In progress | + +### Original Metadata + +Last resort. Use only when the source ticket carries genuinely unclassifiable content that has no home elsewhere (e.g., a scratch field from an intake form that does not map to any other section). Most refinements omit this section entirely. Source-system blocks (Applause, Zendesk, escalation forms) get their facts extracted into the appropriate sections above; the raw block does not survive. + +--- + +## Section Order + +When multiple sections appear, order them like this. Skipped sections close the gap. + +1. Summary +2. Impact +3. Context and Background +4. Affected Scope +5. Reproduction, Expected, Actual +6. Requirements and Acceptance Criteria +7. Questions to Answer +8. Findings +9. Investigation Notes +10. Working Hypotheses +11. Root Cause +12. Workaround +13. Solutions +14. Open Blockers +15. Original Metadata + +The first three positions stay rigid (Summary, Impact, Context) so a reader skimming the top of the ticket sees what, who, and why in that order. Below that, the order matches investigation flow: scope, reproduction, requirements, then evidence and analysis, then resolution, then blockers. diff --git a/jira-issue-triage/skills/jira-ticket-refiner/references/classification-guide.md b/jira-issue-triage/skills/jira-ticket-refiner/references/classification-guide.md new file mode 100644 index 0000000..4d42c59 --- /dev/null +++ b/jira-issue-triage/skills/jira-ticket-refiner/references/classification-guide.md @@ -0,0 +1,61 @@ +# Classification Guide + +Read this when you reach Steps 3 and 4 of the workflow. The first half (information categories) belongs to Step 3. The second half (rewrite principles) belongs to Step 4. + +## Information Categories (Step 3) + +Catalog every distinct fact across the description, every comment, the changelog (when read), and any linked tickets. Each fact gets one category. The category drives where it lands in the template. + +| Category | Examples | +|----------|----------| +| **Observed symptoms** | What users see: errors, missing data, broken UI, wrong totals, slow responses, blank screens | +| **Affected scope** | Customer names, tenant IDs, user IDs, environment (prod / staging / region), browser or device, the population of users hit | +| **Investigation artifacts** | Observability links, log snippets, DB queries, interaction IDs, channel IDs, API request and response samples, screenshots, video recordings | +| **Requirements and acceptance criteria** | Definition of done, feature constraints, success metrics, target user stories, design specs | +| **Decisions and rationale** | Choices made (in comments, threads, meetings) plus the reasoning behind them. Decisions buried in comment threads are the most-frequently-lost piece of context. Surface them. | +| **Unverified analysis** | Hypothesized causes, AI-generated suggestions, "I think it's X because..." reasoning, proposed fixes that nobody has tested | +| **Source-system metadata** | Support escalation tables, Zendesk/Salesforce ticket fields, intake forms, Applause exports, PagerDuty incident records | +| **Status updates from comments** | Progress notes, blockers, handoff context, "we paused this for a week", "Bob took it over". Summarize these into the description. Do NOT restate the ticket's current Jira status, assignee, or priority since those are visible in the UI. | + +## Verified vs Unverified Flag + +Every fact also carries one of two flags: + +- **Verified.** Direct evidence supports it. A log entry shows the 500. A query result names the customer. A screenshot proves the UI is broken. The reporter said it. +- **Unverified.** Reasoning, hypothesis, or speculation. "The root cause is probably the token rotation" is unverified, even when the speaker sounds confident. + +The flag determines section placement in Step 4. Unverified facts go into Working Hypotheses. Verified facts go into the section their content matches. + +## Rewrite Principles (Step 4) + +Apply these when you turn the inventory into prose. + +| Principle | Detail | +|-----------|--------| +| **Symptoms over solutions** | Lead with what users experience or what needs to happen. Do not lead with a proposed fix, even when the proposed fix is in the original. | +| **Evidence over claims** | Logs, error strings, query results, and screenshots are evidence. Frame everything else as a working hypothesis. | +| **Decisions are first-class** | A decision made in a comment thread is invisible. Pull it into the description body with the rationale. | +| **Surface affected scope** | Pull customer names, tenant IDs, environments, and population-of-users-affected from comments into the description body, near the top. | +| **Preserve every artifact** | Customer IDs, log links, query results, error strings, screenshots, attachments, video links: every one survives. Reorganize. Do not delete. | +| **Prose over tables** | Write prose for one to three items. Use a table only when there are four or more rows with distinct columns that would be hard to scan as sentences. A two-row table is almost always worse than a sentence. | +| **Track dependencies** | When the ticket is blocked on or by external parties, name them in an Open Blockers section. One blocker is a sentence. Three or more is a table. | +| **Clean up the noise** | Remove abandoned exploratory queries, conversational framing ("I tried to look into this..."), duplicate separators, and stale "@channel any updates?" pings. | +| **Context over assumption** | A reader with no prior knowledge should be able to read the refined ticket and understand it without chasing external sources. Add the one sentence of background that makes the rest make sense. | +| **No fix prescription** | Strike `Immediate Fix Required`, `Recommended Fix`, `Longer-Term Recommendations`, and similar prescriptive sections. The team that owns the work decides the approach. | +| **No sidebar restate** | Status, priority, type, epic, assignee, reporter, labels, and components live in the Jira sidebar. Do not duplicate them in the body. The exception is comment-thread context, which is hidden behind a tab. | + +## Common Patterns + +These three patterns show up on almost every refinement. + +### Pattern 1: A buried decision + +The original description says nothing about ownership. Comment 4 says "Discussed in standup, Platform team will own the rollback plan." The refined description carries that into the Open Blockers section with the date the decision was made and the team named. + +### Pattern 2: An unverified root cause stated as fact + +The original description says "Root cause: stale auth token after deploy." There is no log entry or query result confirming this. The refined description moves this to Working Hypotheses, framed as: "The most likely cause based on the timeline is stale auth tokens after the 14:00 deploy. This has not been confirmed; verifying requires a log query against the auth service for that window." + +### Pattern 3: Evidence trapped in a comment thread + +Comment 7 contains a Datadog link with 200 lines of relevant errors. The refined description moves the link into Investigation Notes under Observability, names the time window the link covers, and summarizes the error pattern in one sentence. diff --git a/jira-issue-triage/skills/jira-ticket-refiner/references/gathering-guide.md b/jira-issue-triage/skills/jira-ticket-refiner/references/gathering-guide.md new file mode 100644 index 0000000..e0602c4 --- /dev/null +++ b/jira-issue-triage/skills/jira-ticket-refiner/references/gathering-guide.md @@ -0,0 +1,87 @@ +# Gathering Guide + +Read this when you reach Step 1 of the workflow. Skip it on earlier steps. + +## What to Fetch + +Pull every artifact in parallel where the MCP allows it. The fetch is the foundation; underfetching is the most common cause of a refined ticket that loses information. + +1. **Ticket fields and description.** Call `getJiraIssue` once with these fields: + + ``` + summary, description, status, issuetype, priority, labels, + components, assignee, reporter, created, updated, parent, issuelinks + ``` + + Use `responseContentFormat: "markdown"` so the description comes back as markdown rather than ADF JSON. Markdown is the format `editJiraIssue` expects when writing the standard `description` field back. Some rich-text custom fields (notably a "Bug Description" field on Jira instances configured for it) require raw ADF instead and are written via a separate `editJiraIssue` call; see `references/jira-formatting.md` for that path. + +2. **Comments.** Fetch the full comment thread. Comments are where decisions, status updates, customer back-channels, and investigation artifacts go to die. Pulling the description without the comments is the single most common failure mode of a ticket refiner. + +3. **Changelog.** Fetch with `expand: "changelog"` only when the current description looks empty, templated, or sparser than the comments suggest it should be. Earlier description versions often hold detail that was lost during a careless edit. + +4. **Linked tickets.** Walk every entry in `issuelinks` from step 1. For each linked ticket, capture: key, link type (`Duplicate`, `Relates`, `Blocks`, `is blocked by`), summary, and status. A linked ticket sometimes carries the scope or the workaround that the current ticket only hints at. + +If the calling context (the `jira-issue-triage` agent in Phase 5) already passed in a fetched payload, reuse it. Do not refetch. + +## Fetch for Context, Not for Output + +Most of the fields above exist so you can understand the ticket. They do not belong in the refined description. Jira shows them in the sidebar and header already, and restating them is redundant noise. + +| Field | Where it appears in the Jira UI | +|-------|----------------------------------| +| `status` | Header pill | +| `issuetype` | Header icon and label | +| `priority` | Sidebar | +| `parent` | Breadcrumb and sidebar | +| `assignee` | Sidebar | +| `reporter` | Sidebar | +| `labels` | Sidebar | +| `components` | Sidebar | + +The single exception is **comments**. Comment content is hidden behind a separate tab in the Jira UI and easily missed by anyone not following the ticket in real time. Surfacing comment-thread decisions, investigation findings, and meaningful status changes into the description body is the primary value of this skill. + +## Completeness Gate + +Do not advance to Step 2 (archetype classification) until you have: + +- [ ] Read the full description. +- [ ] Read every comment. +- [ ] Read the changelog if Step 1 flagged it as worth checking. +- [ ] Walked every link in `issuelinks` and noted what each linked ticket adds. + +Skipping any of these is how facts get lost in the rewrite. + +## Per-Archetype Priorities + +The archetype determined in Step 2 changes which artifacts matter most. Use this table as a hint when the ticket is dense and you need to prioritize what to read carefully. + +| Archetype | What to surface first | +|-----------|------------------------| +| **Bug** | Error strings (verbatim), reproduction steps, affected users and environments, observability links, customer IDs | +| **Feature** | Acceptance criteria, design docs and product briefs, stakeholder decisions, UX requirements, parent epic context | +| **Task** | Definition of done, why-now context, dependencies on other work, migration steps, rollback plan | +| **Incident** | Timeline (start, detection, mitigation, resolution), blast radius, affected tenants, communication log | +| **Spike** | Open questions, exploration boundaries, constraints, preliminary findings or benchmarks | + +## ADF Content-Loss Check + +The MCP server reads the description as ADF and converts it to markdown for you. Markdown does not have a representation for several ADF features. Those features are silently dropped on the round trip when the rewrite is converted back to ADF and saved. There is no error. There is no warning. The content just disappears. + +Skim the original description and the comments for ADF-only features before you rewrite. Make a note of any you find. The decision to warn the user about content loss happens at preview time (Step 7). + +| ADF feature | Frequency on real tickets | +|-------------|----------------------------| +| Smart Links (Jira/Confluence URL cards) | Very common | +| `@mentions` of users | Very common | +| Info / Warning / Error panels with content | Common | +| Status lozenges (`In Progress`, `Done`) | Common | +| Expand / collapse sections with content | Common | +| Task lists with checked items | Common | +| Embedded media, attached images, video | Common | +| Tables with merged or color-tinted cells | Moderate | +| Date pickers | Moderate | +| Multi-column layouts | Moderate | + +Warn the user only when a loss would be **substantive**. Substantive means the user could lose meaningful information or workflow affordances: a panel containing real instructions, a task list whose checked state matters, an expand section hiding actual content, an embedded video or screenshot, a colored table that conveys data through its color. Cosmetic-only losses (a smart-link card becoming a plain URL) do not need a warning. + +When you do warn, keep it to one sentence outside the preview. The preview itself stays focused on the rewritten ticket content. diff --git a/jira-issue-triage/skills/jira-ticket-refiner/references/jira-formatting.md b/jira-issue-triage/skills/jira-ticket-refiner/references/jira-formatting.md new file mode 100644 index 0000000..33cf9f4 --- /dev/null +++ b/jira-issue-triage/skills/jira-ticket-refiner/references/jira-formatting.md @@ -0,0 +1,95 @@ +# Jira Formatting Reference + +Read this when you reach Step 5 (Apply the Template). Skip it on earlier steps. + +The Atlassian MCP server takes plain markdown for the description and converts it to ADF (Atlassian Document Format) under the hood. This file documents which markdown features survive the conversion intact, which to use with caution, and which are forbidden because they break the call. + +## API Format Rule + +`editJiraIssue` and `createJiraIssue` both accept the description as a markdown **string**. Always pass markdown. Never pass an ADF JSON object: the server still tries to convert it as markdown, fails to parse the JSON, and returns `Failed to convert markdown to adf`. + +The description content the markdown converter receives must contain real line breaks. Construct it like this: + +```markdown +## Heading + +Paragraph with **bold**. +``` + +When that string is sent through a JSON-encoded API request, the JSON encoder represents each real newline as the two-character sequence `\n` inside the wire payload. The JSON parser on the server side decodes those escapes back to real newlines before the markdown converter runs. That is fine and expected: + +```json +{ "fields": { "description": "## Heading\n\nParagraph with **bold**." } } +``` + +The forbidden case (covered in the Forbidden table below) is when the description content itself contains a literal backslash followed by the letter `n` after JSON decoding. That shows up on the rendered ticket as the two characters `\n` instead of a line break. + +When calling `editJiraIssue`, set `contentFormat: "markdown"` if the parameter is required by the MCP version in use. Some Jira custom fields (e.g., a "Bug Description" rich-text field) reject markdown and require raw ADF; that case is handled by a separate `editJiraIssue` call and is not the subject of this reference. + +## Confirmed Safe + +These features have been tested through the MCP markdown-to-ADF converter and survive the round trip cleanly. Use them freely. + +| Feature | Markdown syntax | Notes | +|---------|------------------|-------| +| Headings | `##`, `###`, `####`, etc. | Levels 1 through 6 all render. | +| Bold | `**text**` | | +| Italic | `*text*` or `_text_` | | +| Inline code | `` `code` `` | | +| Fenced code blocks | Triple backticks with optional language tag (e.g. ` ```sql ` ... ` ``` `) | Language tags supported: `sql`, `json`, `python`, `bash`, `yaml`, and others. | +| Numbered lists | `1.`, `2.`, ... | | +| Bullet lists | `-` or `*` | | +| Links | `[text](url)` | | + +## Likely Safe + +Documented as supported in [Atlassian's markdown reference](https://support.atlassian.com/jira/docs/markdown-and-keyboard-shortcuts/) but less thoroughly battle-tested through the MCP converter. Use these confidently and verify in the preview. + +| Feature | Markdown syntax | +|---------|------------------| +| Blockquotes | `>` | +| Horizontal rules | `---` or `***` | +| Tables | `| col | col |` with the header separator row | +| Strikethrough | `~~text~~` | +| Inline images | `![alt](url)` | +| Nested lists | Indent the child list by two spaces under its parent item | + +## Confirmed Broken + +| Feature | Markdown syntax | What actually happens | +|---------|------------------|------------------------| +| Interactive checkboxes | `- [ ]`, `- [x]` | Render as escaped bracket text inside a bullet list. They are not interactive. Do not use. | + +## Forbidden + +These either crash the API or render as literal text on the ticket. Avoid them. + +| Pattern | Why it fails | +|---------|---------------| +| Raw ADF JSON in the description field | Server tries to parse the JSON as markdown, fails, returns `Failed to convert markdown to adf`. | +| HTML tags (`
`, ``, `
`, ``, etc.) | Escaped to literal text. The angle brackets show up on the ticket. | +| Wiki markup (`{code}`, `{panel}`, `h1.`, `||header||`) | Jira Cloud deprecated wiki markup. Renders as literal text. | +| Literal `\n` characters inside the description content (after JSON decoding) | Jira renders them as the two characters backslash-n, not a line break. Build the description with real newlines; the JSON `\n` escape sequence on the wire is a different layer and is handled by the JSON parser before this rule applies. | +| Nested blockquotes (`> > text`) | Inconsistent rendering; some render as a single quote, some as nested. Avoid. | + +## ADF Content-Loss Warning + +The MCP converter reads the existing description as ADF and presents a markdown view of it. ADF features that have no markdown equivalent are silently stripped on the way back through. The API does not warn. The user does not see anything go wrong. The content just disappears. + +Common features at risk on a typical refinement: + +| ADF feature | Frequency | +|-------------|-----------| +| Smart Links (Jira/Confluence URL cards) | Very common | +| `@mentions` of users | Very common | +| Info / Warning / Error panels with content | Common | +| Status lozenges (`In Progress`, `Done`) | Common | +| Expand / collapse sections with content | Common | +| Task lists with checked items | Common | +| Embedded media, attached images, video | Common | +| Date pickers | Common | +| Tables with merged or color-tinted cells | Moderate | +| Multi-column layouts | Moderate | +| Jira issue macros (in Confluence) | Common in Confluence | + +Warn the user only when the loss is **substantive** (a panel with real instructions, a task list whose checked state matters, an embedded video, a colored table that conveys data through color). Keep that warning to one sentence outside the preview. A smart-link card becoming a plain URL is cosmetic and does not need a warning. diff --git a/jira-issue-triage/skills/jira-ticket-refiner/references/title-guide.md b/jira-issue-triage/skills/jira-ticket-refiner/references/title-guide.md new file mode 100644 index 0000000..a02477e --- /dev/null +++ b/jira-issue-triage/skills/jira-ticket-refiner/references/title-guide.md @@ -0,0 +1,101 @@ +# Title Guide + +Read this when you reach Step 6 of the workflow. Skip it on earlier steps. + +## Why the Title Matters More Than the Description + +The title is the highest-leverage piece of text on a ticket. It shows up in: + +- Board cards and list views. +- Jira search results. +- Slack unfurls when the ticket URL is pasted. +- Email notifications and digest summaries. +- Sprint planning exports and burndown charts. +- The Jira sidebar of any ticket that links to this one. + +A good title lets a teammate decide whether to open the ticket without clicking through. A vague title forces them to open it just to find out, every time. + +## Rules + +1. **Name the area.** The product surface, system, service, or component. `SSO`, `Billing API`, `Resident Portal`, `CI Pipeline`, `Email Templates`, `Visitor Management`. +2. **State the problem or goal.** What is broken, what needs to happen, or what question is being investigated. +3. **Add the differentiator.** If there are ten SSO tickets in the project, the title for this one says what makes it specific. +4. **Stay under 80 characters.** Jira truncates long summaries on board and list views. Aim for 60 to 75 characters in the rewritten title; treat 80 as the hard ceiling. +5. **Strip the generic words.** Titles that read `Bug`, `Issue`, `Broken`, `Request`, `Needs Fix`, or just a feature name with no verb carry no information and trip the differentiator rule. + +## Pattern + +Use this base pattern. Add modifiers when the ticket warrants them. + +``` +{Area}: {specific problem or goal} +``` + +Customer-specific bug: + +``` +{Area} + {Customer}: {specific problem} +``` + +Incident: + +``` +P{n}: {Area} {short problem statement} +``` + +Spike: + +``` +Spike: {Area} {question to answer} +``` + +The colon is the structural separator. Avoid em dashes and spaced hyphens here as in the body. + +## Examples by Archetype + +Each example shows the original title, then the rewritten title. + +### Bugs + +| Original | Rewritten | +|----------|-----------| +| `VMS issues` | `VMS: Visitor notifications not sending for scheduled visits` | +| `SSO broken` | `SSO + Cushman Wakefield: Azure AD login fails with 401` | +| `Missing Templates` | `Email Templates: Missing after property onboarding for Brookfield` | +| `Login bug` | `Resident Portal: Login redirect loops on Safari iOS` | + +### Features and Stories + +| Original | Rewritten | +|----------|-----------| +| `Bulk invite` | `Resident Portal: Add bulk-invite flow for move-in events` | +| `Dashboard updates` | `Analytics Dashboard: Add occupancy trend chart to property overview` | +| `Better filters` | `Lease Search: Add filter for lease end date and tenant status` | + +### Tasks and Chores + +| Original | Rewritten | +|----------|-----------| +| `Update Jest` | `CI: Migrate Jest config from v28 to v30` | +| `Clean up old code` | `Billing Service: Remove deprecated v1 payment processor adapter` | +| `Bump deps` | `Resident Portal: Upgrade Next.js from 14 to 15 and resolve breaking changes` | + +### Incidents + +| Original | Rewritten | +|----------|-----------| +| `Payments down` | `P1: Payment processing failing for all US properties` | +| `Site slow` | `P2: Resident Portal page load times above ten seconds during peak hours` | +| `Email outage` | `P2: Outbound notification emails delayed by SES throttling` | + +### Spikes and Investigations + +| Original | Rewritten | +|----------|-----------| +| `SSO research` | `Spike: Evaluate SSO providers for multi-tenant SAML support` | +| `Performance investigation` | `Spike: Profile and identify bottleneck in lease renewal flow` | +| `Mobile push spike` | `Spike: Compare FCM and APNs for in-app notifications` | + +## When to Keep the Original + +If the original title already follows the pattern, says the area clearly, and would not gain anything from a rewrite, leave it alone. The rule is to make the title better, not to rewrite for its own sake. The preview should still show the title even when unchanged so the user can confirm. diff --git a/jira-issue-triage/skills/requirements-investigator/SKILL.md b/jira-issue-triage/skills/requirements-investigator/SKILL.md new file mode 100644 index 0000000..c7bb78d --- /dev/null +++ b/jira-issue-triage/skills/requirements-investigator/SKILL.md @@ -0,0 +1,136 @@ +--- +name: requirements-investigator +description: "Investigates a non-bug Jira ticket (Feature, Task, Spike) by reading the ticket and linked design or product docs, searching Slack and Confluence for prior decisions, and producing an evidence-tagged orientation report. Use when a developer is about to pick up a Feature, Task, or Spike ticket and wants context before starting work." +metadata: + author: Taha Bikanerwala +tools: Read, Bash, Grep, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql, mcp__plugin_atlassian_atlassian__searchConfluenceUsingCql, mcp__plugin_slack_slack__slack_search_public_and_private, mcp__plugin_slack_slack__slack_read_thread +--- + +# Requirements Investigator + +Produce a structured report that orients a developer about to pick up a Feature, Task, or Spike ticket. The report names what is being built (or asked), surfaces prior decisions found in Slack and Confluence, lists the open questions that block start-of-work, and tags every claim with its evidence level. + +This skill investigates. It does not solve, post, or modify anything. + +## Calling Convention + +This skill runs without user interaction. The constraints below let it work cleanly inside the `jira-issue-triage` agent (which has its own confirmation gate) and standalone. + +- **Non-interactive.** Never ask the user a question. Inputs are inferred from the ticket and search results. +- **Predictable structure.** Per-archetype templates with fixed section orders. The archetype is passed by the caller; when running standalone, infer it from the issue type field. +- **Same evidence tags.** Always `[VERIFIED]`, `[OBSERVED]`, `[INFERRED]`, `[UNKNOWN]`. +- **Output is the last thing.** Skill ends after the report renders. No follow-up prompts. +- **Read-only.** No `editJiraIssue`, no `addCommentToJiraIssue`, no `slack_send_message`. Posting is the caller's job. + +## Search Ladder + +Investigation runs three levels top to bottom. Each level has a gate: if it produces enough evidence to write a useful report, skip the remaining levels. Datadog is not in the ladder by default; non-bug tickets rarely have runtime telemetry to query, and adding it pulls in a tool the skill does not need for this archetype. + +### Setup + +Before running the levels, fetch the ticket data once and cache it for the rest of the skill. + +1. Identify the ticket key from the invocation context (e.g., `PROJ-1234` from a pasted URL or a parameter passed by the caller). If the calling context (such as the `jira-issue-triage` agent) has already fetched the ticket and exposed the payload, reuse that payload; don't fetch again. +2. If no payload is available, call `getJiraIssue` with the ticket key and `responseContentFormat: "markdown"`. Request these fields at minimum: `summary`, `description`, `status`, `issuetype`, `labels`, `components`, `assignee`, `reporter`, `created`, `updated`, `parent`, `issuelinks`. Add the optional custom fields the caller cares about (Sprint, Story Points, Acceptance Criteria, Scope Summary) when known. +3. Determine the archetype: use the value passed by the caller. If running standalone, map `issuetype.name` using the table in the `jira-issue-triage` agent body (Story/Feature/Enhancement -> Feature; Task/Sub-task/Chore/Tech Debt -> Task; Spike/Research/Investigation -> Spike). If the issue type is `Bug` or `Incident`, stop and tell the caller this skill does not handle that archetype; route to `issue-investigator` instead. +4. Cache the response as "the ticket payload" throughout the skill. + +If `getJiraIssue` fails (auth error, ticket not found, network), stop and tell the caller which call failed. Do not proceed without ticket data. + +### Level 1: Slack + +Run 2-3 queries via `slack_search_public_and_private`: + +1. The ticket key (e.g., `PROJ-1234`). +2. The feature, task, or spike name, or the most distinctive phrase from the summary. +3. The area or system name combined with a key term from the description. + +For each relevant hit, follow the thread in full with `slack_read_thread`. + +What you are looking for: +- A prior decision that defines the scope (often labeled "decided", "approved", "agreed"). +- A linked design doc, product brief, or RFC. +- A named owner or Decider for the area. +- A prior thread that names the same problem with a different framing (the team may have a different vocabulary for the same scope). + +**Gate:** if a Slack thread contains a confirmed scope statement, an approved design link, or a clear "out of scope" decision, write the report citing that thread and skip Levels 2-3. + +### Level 2: Ticket + Jira + Confluence + +Read the ticket payload (cached in Setup) carefully. Signals are easy to miss on a fast scan: linked design docs, mentions of related tickets, vocabulary like "blocks" or "depends on" that points at sequencing constraints, the question the reporter is actually asking. + +Then search: + +- **Linked tickets.** Follow every `issuelinks` entry and the `parent` field. Read the related ticket descriptions and the most recent 1-2 comments. Note: status, assignee, the most relevant scope statement. +- **Jira related tickets** via `searchJiraIssuesUsingJql`. Common patterns: + - `project = "" AND text ~ "" ORDER BY created DESC` + - `project = "" AND component = "" AND issuetype in (Story, Feature, Task, Spike) ORDER BY created DESC` + - `project = "" AND parent = "" ORDER BY created DESC` when the ticket has an epic parent. +- **Confluence** via `searchConfluenceUsingCql`. Search for product briefs, design docs, ADRs, RFCs, runbooks for the area. Use the feature area, system name, or product theme as the search term. + +For each related Jira ticket, record: key, summary, status, assignee, the most relevant scope or AC finding. +For each Confluence page, record: URL and a 1-line summary of what it contains. + +**Gate:** if a product brief or design doc explicitly states the scope and acceptance criteria for this work, write the report citing that source. Skip Level 3 unless the ticket also references existing code that needs orientation. + +### Level 3: Code + +Enter only when Levels 1-2 turned up nothing useful, OR the ticket explicitly references existing code that needs context to size or scope the work. + +1. **Affected service or module.** When the ticket names a service, module, or file path, use `Bash` (e.g., `git ls-files | grep -i 'pattern'`) or `Grep` to locate it in the repository. Note the relative path. +2. **Existing patterns.** When the ticket references "the same way we do X for Y", use `Grep` to find the X pattern and `Read` the existing implementation. Record the path and a 1-line summary of the pattern. +3. **Recent changes.** Run `git log --since="2 months ago" -- ` via `Bash` to find commits in the affected area. The recent change list informs the "Where To Look" section. + +Stop when you can name: which area of the code is involved, what existing patterns to follow, and 1-3 specific files or directories the developer should open first. Do not trace full call chains; this is orientation, not implementation. + +## Evidence Model + +Every claim in the report carries one of four tags. + +| Tag | Meaning | +|-----|---------| +| `[VERIFIED]` | Directly confirmed. Read in code or in an authoritative source (design doc, ADR, ticket description) that explicitly states this. | +| `[OBSERVED]` | A pattern matches the reported scope, but reaching the conclusion required a logical step. | +| `[INFERRED]` | Logical deduction from available information. Not directly observed. | +| `[UNKNOWN]` | Cannot determine from available sources. Requires asking a person or reading docs that were not found. | + +If the finished report has more `[INFERRED]` than `[VERIFIED]` findings, the search was insufficient. Go back and search more before writing. + +Every `[UNKNOWN]` becomes either an "Open Questions" item (when the unknown blocks start of work and a person can answer it) or a "Where To Look" item (when the unknown can be resolved by reading or searching). + +## Stop Condition + +Investigation is **done** when all three are true: + +1. The Lead names what the ticket asks for in one or two sentences, with the strongest evidence inline. +2. At least one source has been consulted at every search level the investigation reached. (If Level 1 closed via its gate, Levels 2-3 do not need sources.) +3. There are concrete next-step queries, file paths, or doc links in "Where To Look". + +If any one is missing, keep investigating. + +## Report Template + +The template differs by archetype. Read `references/report-template.md` when you reach the report-writing step. Pick the matching template based on the archetype determined in Setup. + +Section orders and definitions live in `references/report-template.md`. The three templates share five concepts: a one-line Lead, a context section (Background or Why Now or Question to Answer), a scope-or-knowledge section, an unknowns section, and a Where To Look section. + +## Adaptation Rules + +These rules adjust section emphasis without changing the section list. + +- **Found at Level 1 (Slack):** the context section leads with the Slack source and links the thread. Other context sections may be 1 line. +- **Found at Level 2 (design doc or product brief):** the context section leads with the doc URL and quotes the most relevant sentence. Same brevity allowed elsewhere. +- **Required Level 3 (code):** the Where To Look section includes code references as `path/to/file.ext` (no line numbers; this is orientation, not pinpointing). When the existing pattern is the finding, name the file in the context section. +- **Vague ticket (almost no signal):** the Open Questions / Open Blockers section grows to name what the developer would need from the reporter or product owner before starting. Do not pad with prescriptive suggestions; only name genuine unknowns. + +## Writing Rules + +These apply to all text in the report. + +- No em dashes or spaced hyphens as separators. Em dashes inside parenthetical asides are fine. +- No LLM vocabulary: delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate. +- Lead with the answer. No opener phrases. +- No trailing summaries on short sections. +- Prose over bullet lists when the content flows naturally as sentences. +- Never present unverified scope as confirmed acceptance criteria. +- Quote design docs and product briefs directly (single sentence) when paraphrasing risks losing meaning. diff --git a/jira-issue-triage/skills/requirements-investigator/references/report-template.md b/jira-issue-triage/skills/requirements-investigator/references/report-template.md new file mode 100644 index 0000000..b6fb5b2 --- /dev/null +++ b/jira-issue-triage/skills/requirements-investigator/references/report-template.md @@ -0,0 +1,148 @@ +# Report Template + +Read this when you reach the report-writing step in `requirements-investigator`. Skip on earlier steps. + +The report template differs by archetype. Pick the matching template based on the archetype passed by the caller (the agent in Phase 1) or inferred from the issue type when running standalone (Story / Feature / Enhancement -> Feature; Task / Sub-task / Chore / Tech Debt -> Task; Spike / Research / Investigation -> Spike). + +Each template has a fixed section list and order. Do not omit a section heading. If a section has nothing meaningful to say, write a one-line note under the heading ("Not applicable for this ticket" or "Nothing prior found"). The absence is itself signal, and keeping the heading lets a reader scan for the section even when it is empty. + +## Feature Template + +Six sections. + +### 1. Lead + +1-2 sentences. Name what is being built and the single best summary of scope. Inline evidence tag. Do not restate the ticket title. + +Example: + +> Add a per-tenant rate limit for the bulk-export endpoint, default 100 requests per hour, configurable per tenant `[VERIFIED]` from the product brief at PROJ-1234. + +### 2. Background + +2-4 sentences. Why this ticket exists. Prior decisions, related history, links to product briefs, design docs, runbooks, related Jira tickets. Pull this from Confluence and the linked tickets discovered in Level 2. + +When the background is established in a Confluence brief or ADR, lead with the URL and quote the single most relevant sentence. Do not paraphrase across multiple decisions; name them separately. + +### 3. Requirements Found + +Concrete acceptance criteria, definition of done, success metrics, target user stories. Pull from the ticket itself, linked tickets, and any Confluence spec the ticket points to. + +Format as a bulleted list when there are 3 or more items. Each bullet starts with the requirement, followed by the source citation in brackets. Tag explicit gaps as `[UNKNOWN]`. + +Example: + +> - Bulk export rate limit defaults to 100 req/hour per tenant `[VERIFIED]` (PROJ-1234 description, paragraph 2). +> - Override via `bulk_export_rate_limit` config field, integer in requests/hour, no upper bound `[OBSERVED]` (PROJ-1234 comment from @alice 2026-04-15). +> - Rate limit error response shape unspecified `[UNKNOWN]`. + +### 4. Design Refs + +Links to Figma boards, design docs, mockup reviews, ADRs. One bullet per link with a one-phrase summary of what's at the link. If nothing is linked, write "None found in ticket or comments." A Feature ticket with no design refs is itself a triage flag, worth naming. + +### 5. Open Questions + +Genuine unknowns that need an answer before development starts. Each question is specific (not "what's the scope?"). Tag the most likely answerer if discoverable from the ticket history or the Slack search. + +Example: + +> - What error response shape should the rate limit return? Likely answerer: @bob (named in PROJ-1234 thread). `[UNKNOWN]` +> - Does the rate limit apply to admin-impersonated requests? `[UNKNOWN]` + +### 6. Where To Look + +2-5 tool-by-tool items. Each item: + +- Names the tool (code search, Confluence search, Slack search, ticket key, design tool). +- Gives the exact ready-to-paste query, URL, or file path. +- Says in one phrase what a hit or miss tells you. + +Examples: + +> - **Code search:** `rg 'rate_limit' services/bulk_export/` to find existing rate-limit infrastructure. A hit identifies the pattern to follow; a miss means this is greenfield in the bulk-export service. +> - **Slack search:** `from:@alice "bulk export"` to find the design discussion that led to PROJ-1234. A hit gives the rationale behind the 100 req/hour default. +> - **Confluence:** `Bulk Export ADR` (full text) to find the architecture decision record. The ADR is the canonical scope source. + +## Task Template + +Five sections. + +### 1. Lead + +1-2 sentences. Name what needs to happen and the why-now. Inline evidence tag. + +Example: + +> Bump `pg` driver from 8.x to 9.x across the `payments` and `billing` services to unblock Postgres 16 upgrade `[VERIFIED]` from PROJ-2345. + +### 2. Why Now + +2-3 sentences. The trigger for the task: dependency upgrade unblocks something downstream, deprecation deadline, related migration, runbook execution, security advisory. + +Pull from the ticket and recent Slack discussions. When a deadline or dependency is the trigger, name it specifically with date or ticket reference. + +### 3. Definition of Done Found + +Concrete completion criteria. Pull from the ticket and any linked checklist or runbook. Tag explicit gaps as `[UNKNOWN]`. + +Format as a bulleted list. Each bullet is a single completion check, not a process step. + +### 4. Risks + +Anything in the affected code, config, or runbook that makes this task non-trivial. One bullet per risk. Examples: + +- Shared config touched by other teams. +- Dependency with a known breaking change between minor versions. +- Infrastructure resource with downstream consumers (database, queue, shared library). +- The task touches a runbook step that has no documented rollback. + +If no risks are found, write "No specific risks surfaced from search; standard care expected." + +### 5. Where To Look + +Same format as Feature template Section 6. + +## Spike Template + +Five sections. + +### 1. Lead + +1-2 sentences. Name the question being investigated and the time-box if known. Inline evidence tag. + +Example: + +> Spike: evaluate whether Cognito can support more than 100 IdPs per user pool. Time-boxed at 3 days `[VERIFIED]` from PROJ-3456. + +### 2. Question to Answer + +The specific decision or unknown the spike is meant to resolve. Quote it directly from the ticket if the ticket states it well; rewrite if vague. Multiple sub-questions are allowed if they are all in scope. + +Example: + +> Primary question: Does Cognito support more than 100 IdPs per user pool today? `[VERIFIED]` from PROJ-3456. +> Sub-questions: What is the practical performance impact of >100 IdPs? What are the migration paths if the limit is hard? + +### 3. What's Already Known + +Findings from any prior spike, design doc, related ticket, or Slack thread that bear on the question. Tag each with evidence level. If nothing is known, write "Nothing prior found." (which is itself a useful signal: the spike is starting from scratch). + +### 4. What's Unknown + +The gaps that the spike needs to fill. One bullet per gap. Each phrased as a concrete check (`Does Cognito support more than 100 IdPs?`) rather than a vague topic (`scalability`). + +When the team has access to a vendor representative or a person with prior context, name them on the relevant unknown. + +### 5. Where To Look + +Same format as Feature template Section 6. For Spikes, "Where To Look" frequently includes external research (vendor docs, RFCs, comparison articles, vendor support tickets) in addition to internal sources. + +Example: + +> - **Vendor docs:** AWS Cognito IdP service quotas page (https://docs.aws.amazon.com/cognito/latest/developerguide/limits.html) to confirm the documented limit. +> - **Vendor support:** check past AWS Support cases for IdP limit-raise requests; AWS often raises soft limits. +> - **Related code:** `rg 'IdentityProvider' services/auth/` to see how many IdPs are currently configured per pool in the existing codebase. + +## Section Order + +Sections appear in the order shown for each archetype. Never omit a section heading. When a section legitimately has nothing to say, keep the heading and write the brief placeholder line described at the top of this file. If the absence is itself important context (e.g., a Feature ticket with no design refs), call it out in the Lead in addition to the placeholder line in the empty section. From c82c516ae281f4caecd713cebcb5d5deb9a473dd Mon Sep 17 00:00:00 2001 From: TahaBikanerwala Date: Mon, 4 May 2026 16:13:33 +0530 Subject: [PATCH 2/6] feat(jira-issue-triage): wire prose-style into Phase 5 and bump to v1.1.0 Co-Authored-By: Claude Opus 4.7 (1M context) --- jira-issue-triage/.claude-plugin/plugin.json | 4 +- jira-issue-triage/README.md | 15 +- jira-issue-triage/agents/jira-issue-triage.md | 69 ++++---- jira-issue-triage/commands/setup.md | 13 +- jira-issue-triage/skills/prose-style/SKILL.md | 71 ++++++++ .../prose-style/references/anti-patterns.md | 164 ++++++++++++++++++ .../skills/prose-style/references/examples.md | 93 ++++++++++ 7 files changed, 378 insertions(+), 51 deletions(-) create mode 100644 jira-issue-triage/skills/prose-style/SKILL.md create mode 100644 jira-issue-triage/skills/prose-style/references/anti-patterns.md create mode 100644 jira-issue-triage/skills/prose-style/references/examples.md diff --git a/jira-issue-triage/.claude-plugin/plugin.json b/jira-issue-triage/.claude-plugin/plugin.json index 65881fe..dec68dd 100644 --- a/jira-issue-triage/.claude-plugin/plugin.json +++ b/jira-issue-triage/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "jira-issue-triage", - "version": "1.0.0", - "description": "End-to-end Jira issue triage subagent across all archetypes (Bug, Incident, Feature, Task, Spike). Bundles three skills (issue-investigator for Bug/Incident, requirements-investigator for Feature/Task/Spike, jira-ticket-refiner for any archetype) and ships a /jira-issue-triage:setup wizard.", + "version": "1.1.0", + "description": "End-to-end Jira issue triage subagent across all archetypes (Bug, Incident, Feature, Task, Spike). Bundles four skills (issue-investigator for Bug/Incident, requirements-investigator for Feature/Task/Spike, jira-ticket-refiner for any archetype, prose-style for writing-rule application) and ships a /jira-issue-triage:setup wizard.", "author": { "name": "Taha Bikanerwala", "url": "https://github.com/TahaBikanerwala" diff --git a/jira-issue-triage/README.md b/jira-issue-triage/README.md index 81b1361..057d354 100644 --- a/jira-issue-triage/README.md +++ b/jira-issue-triage/README.md @@ -28,15 +28,16 @@ The config file moves from `.claude/jira-bug-triage.config.json` to `.claude/jir ### Bundled skills -The agent calls three skills during the workflow. All three ship bundled with this plugin and install automatically. +The agent calls four skills during the workflow. All four ship bundled with this plugin and install automatically. | Skill name | Phase | Used for | Status | |-----------|-------|----------|--------| | `issue-investigator` | Phase 1 (Bug, Incident) | Slack/Jira/Confluence/Datadog/code investigation with evidence tags | Bundled, ready to use | | `requirements-investigator` | Phase 1 (Feature, Task, Spike) | Slack/Jira/Confluence search for prior decisions, design refs, scope; per-archetype report templates | Bundled, ready to use | | `jira-ticket-refiner` | Phase 5 (any archetype) | Title and description rewrite. Already archetype-aware (Bug, Feature, Task, Incident, Spike). | Bundled, ready to use | +| `prose-style` | Phase 2.5 + Phase 5 (any archetype) | Writing-rule application: strips em dashes, opener phrases, LLM vocabulary, bullet sprawl. Runs at Phase 2.5 on the assessment/scope comment draft and any reporter follow-up (so the user sees a styled version at the Phase 3 confirmation gate), and at Phase 5 on the refined title and description after `jira-ticket-refiner`. | Bundled, ready to use | -The `prose-style` skill (writing-rule application) is planned as a separate plugin in the same marketplace. Until it ships, the agent uses an inline fallback that enforces the same writing rules and warns you once at the start of Phase 5. +The agent body retains short defensive fallbacks for all four bundled skills. They fire only on rare runtime load failures (corrupted install, mid-session uninstall, etc.) and never need user attention in normal operation. ## Quick start @@ -225,12 +226,12 @@ The workflow runs a generic core for every archetype. Five phases gate on the de | Phase 0 | Fetch ticket, run skip-label check, detect archetype, assign to you, transition to investigating. | All | | Phase 1 | Investigation: `issue-investigator` (Bug/Incident) or `requirements-investigator` (Feature/Task/Spike). | All (skill choice gates on archetype) | | Phase 2 | Datadog log search using signals from Phase 1. Silently suppressed on errors. | Bug, Incident | -| Phase 2.5 | Decide whether reporter follow-up is warranted (missing data / clarification / fix verification or relevance check). Form severity recommendation (Bug/Incident) or scope summary (Feature/Task/Spike). | All | +| Phase 2.5 | Decide whether reporter follow-up is warranted (missing data / clarification / fix verification or relevance check). Form severity recommendation (Bug/Incident) or scope summary (Feature/Task/Spike). Draft the matching Phase 4 comment (assessment, scope summary, or follow-up question) in markdown, then run `prose-style` on it so Phase 3 previews a styled draft. | All | | Phase 3 | **Hard pause.** Show findings, archetype detection, and proposed updates. Wait for your approval. | All | -| Phase 4a | Severity assessment comment (ADF). | Bug, Incident | -| Phase 4b | Scope or AC summary comment (ADF). Optionally writes to `scope_summary_field_name` if configured. | Feature, Task, Spike | -| Phase 4c | Follow-up question tagging reporter or EM. Replaces Phase 4a or 4b. | All (only when follow_up_needed) | -| Phase 5 | Refine ticket via `jira-ticket-refiner`. Update title and description. | All | +| Phase 4a | Convert the Phase 2.5 cleaned draft to ADF and post the severity assessment comment. | Bug, Incident | +| Phase 4b | Convert the Phase 2.5 cleaned draft to ADF and post the scope or AC summary comment. Optionally writes to `scope_summary_field_name` if configured. | Feature, Task, Spike | +| Phase 4c | Convert the Phase 2.5 cleaned draft to ADF and post the follow-up question tagging reporter or EM. Replaces Phase 4a or 4b. | All (only when follow_up_needed) | +| Phase 5 | Refine ticket via `jira-ticket-refiner`, then run `prose-style` on the refined title + description, then preview and update. | All | | Phase 6 | Severity + due date (Bug/Incident) OR optional sprint placement + story points (Feature/Task/Spike). Skipped on follow-up path. | All (behavior gates on archetype) | | Phase 7 | Link related/duplicate tickets. | All | | Phase 8 | Append triaged label. Fill optional fields if discoverable. | All | diff --git a/jira-issue-triage/agents/jira-issue-triage.md b/jira-issue-triage/agents/jira-issue-triage.md index 121fbcb..9d4d055 100644 --- a/jira-issue-triage/agents/jira-issue-triage.md +++ b/jira-issue-triage/agents/jira-issue-triage.md @@ -89,14 +89,9 @@ The agent invokes other skills during the workflow. Reference them by name; the | Phase 1 (Bug, Incident) | `issue-investigator` | Search Slack, the ticket and related Jira/Confluence pages, Datadog, then code if needed. Produces an evidence-tagged report in the 6-section bug-archetype template. | | Phase 1 (Feature, Task, Spike) | `requirements-investigator` | Search Slack and Confluence for prior decisions, read linked design and product docs, search related Jira tickets. Produces an evidence-tagged report in the matching archetype template (Feature, Task, or Spike). | | Phase 5 (any archetype) | `jira-ticket-refiner` | Restructure the ticket description into a clear, self-contained document. Works for any archetype. Updates the title and description via the Atlassian MCP and never deletes original content. | +| Phase 2.5 + Phase 5 (any archetype) | `prose-style` | Audit and rewrite drafted text so it reads like a person wrote it. Phase 2.5 invocation: clean the assessment/scope comment draft and any reporter follow-up before the Phase 3 preview gate. Phase 5 invocation: clean the refined title and description after `jira-ticket-refiner` runs and before the user-facing preview. Strips AI tells: em dashes, opener phrases, LLM vocabulary, bullet sprawl. | -**Future separate plugins** (planned; not yet shipped in this marketplace): - -| Phase | Skill name | Purpose | -|-------|-----------|---------| -| Phase 5 | `prose-style` | Apply writing rules to comment text and refined descriptions. | - -If a skill is not installed when the agent tries to call it, the `Skill` tool returns an error. Fall back to the brief inline summary in the relevant phase and warn the user once at the start of that phase. +All four bundled skills install with the plugin. The defensive fallbacks below fire only on rare runtime load failures; they are not the expected execution path and never need user attention in normal operation. ## Connections @@ -179,21 +174,17 @@ Use the matching template. Keep each question specific. One tightly scoped quest > @{Reporter or EM display name} > -> Thanks for filing this. To triage it properly we need one more detail: -> > {one specific question, e.g., "What user email or ID was affected?" or "Which browser and version were you using when this happened?"} > -> Reply here when you have it and we'll pick this back up. Transitioning to {waiting_reply transition} in the meantime. +> We need this to triage the ticket. Reply here when you have it and we'll pick this back up. Transitioning to {waiting_reply transition} in the meantime. **Clarification:** > @{Reporter or EM display name} > -> Thanks for filing this. Before we investigate further, can you confirm: -> > {specific clarifying question. Quote the part of the description that's ambiguous and offer a concrete this-or-that.} > -> The context you gave points in two different directions and we want to chase the right one. Transitioning to {waiting_reply transition}. +> The ticket points in two different directions and we want to chase the right one. Transitioning to {waiting_reply transition}. **Fix verification (Bug or Incident):** @@ -213,9 +204,9 @@ Use this variant when the archetype is non-bug and the ticket appears stale (no > > Is this still on your team's roadmap? If not, we'll close it. Transitioning to {waiting_reply transition}. -Rules for all three templates: +Rules for all four templates: - Lead with the request or the evidence. No opener phrases, no restating the title, no apologies. -- Apply the Writing Rules at the bottom. No em dashes, no LLM vocabulary. +- Phase 2.5 runs the `prose-style` skill on the filled-in template before the Phase 3 preview, so the reporter or EM sees a styled draft and the user reviews it once. The Writing Rules section at the bottom of this file is the defensive fallback when the skill does not load. - Never chain multiple questions. If you need more than one piece of information, pick the one that unblocks triage and leave the rest for the owning team. - State explicitly that you're moving the ticket to `waiting_reply` so the reporter knows what to expect. - Tag only the reporter (or their EM). Do not add other tags in the question comment. @@ -324,15 +315,20 @@ Build a Logs URL for the engineer: Decide whether a reporter follow-up is warranted before presenting findings. This is the only place the follow-up decision is made. Universal across archetypes. 1. Apply the criteria in **Reporter Follow-up Policy** above. On non-bug archetypes, "fix verification" reframes as "still relevant?" (the ticket may have been overtaken by other work). -2. **For Bug or Incident: form a severity recommendation** using the Severity Criteria table at the top of this file. Match the ticket's evidence to the dimensions (User impact, Functional impact, Workaround, Data integrity, Compliance) and pick the closest level from `severity_scheme`. Cache the recommendation so Phase 3 can display it and Phase 4a can use it. **For Feature, Task, or Spike: skip this severity step** (severity does not apply); instead form a one-line scope summary that captures what the ticket covers and what is unclear, ready for Phase 4b to expand into a comment. -3. **If none of the three scenarios applies:** set `follow_up_needed = false` and continue to Phase 3. -4. **If one applies:** - - Set `follow_up_needed = true` and record the scenario (missing data, clarification, fix verification or relevance check). - - Identify who to tag using **Identifying who to tag**. Cache the target `accountId` and whether the EM preamble applies. - - Draft the question comment using the matching template. Keep it to one specific question. - - If you need to pause to ask the user for an EM, do that now before reaching Phase 3. +2. **For Bug or Incident: form a severity recommendation** using the Severity Criteria table at the top of this file. Match the ticket's evidence to the dimensions (User impact, Functional impact, Workaround, Data integrity, Compliance) and pick the closest level from `severity_scheme`. Cache the recommendation so Phase 3 can display it. On the standard path (`follow_up_needed = false`), Phase 4a uses the same value in the comment body and Phase 6 uses it to compute the due date. On the follow-up path, the recommendation is still cached for Phase 3 context, but Phase 4a and Phase 6 are skipped. **For Feature, Task, or Spike: skip this severity step** (severity does not apply); instead form a one-line scope summary that captures what the ticket covers and what is unclear, ready for Phase 4b to expand into a comment. Cache it for Phase 3 display. +3. **Decide the follow-up path now, before drafting the comment.** + - If none of the three follow-up scenarios applies: set `follow_up_needed = false` and continue to step 4. + - If one applies: set `follow_up_needed = true` and record the scenario (missing data, clarification, fix verification or relevance check). Identify who to tag using **Identifying who to tag** and cache the target `accountId` plus whether the EM preamble applies. If you need to pause to ask the user for an EM, do that now before continuing to step 4. +4. **Draft only the Phase 4 comment that will actually be posted** (still in markdown shape, not yet ADF). The branch is set by `follow_up_needed`: + - `follow_up_needed = false`, Bug or Incident: draft the assessment body using the Phase 4a structure (Assessment, Severity Recommendation, Evidence from this ticket, Criteria matched). Phase 4a will post this. + - `follow_up_needed = false`, Feature, Task, or Spike: draft the scope summary body using the Phase 4b structure (Scope Summary, What's in scope, Evidence from this ticket, Open questions). Phase 4b will post this. + - `follow_up_needed = true` (any archetype): draft the question comment using the matching template from **Question comment templates** above. Keep it to one specific question. Phase 4c will post this. Phase 4a and 4b are skipped on this path, so do not draft an assessment or scope summary. + + Cache the resulting markdown draft. +5. **Run the `prose-style` skill on the drafted comment text from step 4.** Pass the markdown draft as input via the `Skill` tool with `name: "prose-style"`. Replace the cached draft with the returned cleaned version. Phase 3 displays the cleaned markdown draft to the user. Phase 4a, 4b, or 4c (whichever applies) converts the same cleaned text into ADF nodes at posting time. + - **Defensive fallback when `prose-style` does not load:** apply these rules inline to the draft before caching: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists when the content flows naturally as sentences. Warn the user once at the start of Phase 3 that the fallback was used. -Record the decision and draft so Phase 3 can show the user both the investigation findings and the proposed follow-up in one review. +Record the decisions and the cleaned draft so Phase 3 can show the user the investigation findings, the proposed Phase 4 comment, and the proposed follow-up (if any) in one review. --- @@ -343,12 +339,12 @@ Present findings to the user. Show: - The detected archetype (Bug / Incident / Feature / Task / Spike) and the rule that drove the detection (issue type vs content). State this in one short line at the top so the user can override before any irreversible work runs. - Investigation report summary (key findings, hypotheses, evidence tags). - Datadog findings, only if Phase 2 ran AND returned usable data. -- **Bug/Incident:** Proposed severity recommendation and computed due date. The drafted severity assessment comment text (the full ADF body, rendered for review), exactly as it will appear on the ticket. This is the proposed Phase 4a content. -- **Feature/Task/Spike:** The drafted scope summary comment text (the full ADF body, rendered for review), exactly as it will appear on the ticket. This is the proposed Phase 4b content. If `sprint_field_name` or `story_points_field_name` is configured, also display the proposed sprint placement / story-point estimate. +- **Bug/Incident, `follow_up_needed = false`:** Proposed severity recommendation and computed due date. The prose-style-cleaned markdown draft of the assessment comment from Phase 2.5, shown inline as plain markdown. Phase 4a will convert this same text to ADF on post; the ADF rendering preserves the headings, bullets, and inline links the markdown shows. This is the proposed Phase 4a content. +- **Feature/Task/Spike, `follow_up_needed = false`:** The prose-style-cleaned markdown draft of the scope summary comment from Phase 2.5, shown inline as plain markdown. Phase 4b will convert this same text to ADF on post. This is the proposed Phase 4b content. If `sprint_field_name` or `story_points_field_name` is configured, also display the proposed sprint placement / story-point estimate. - If `follow_up_needed = true`: the follow-up plan as a distinct block: - Scenario (missing data / clarification / fix verification or relevance check). - Who will be tagged (reporter or EM) and why. - - The exact comment text you drafted, rendered as it will appear on the ticket. + - The prose-style-cleaned markdown draft of the question comment from Phase 2.5, shown inline as plain markdown. Phase 4c will convert this same text to ADF on post. - What transition will happen (`waiting_reply`), who the ticket will be assigned to (the tagged person), and what will still run (refine, link, label) vs. skipped (the archetype-specific Phase 4 content, severity + due date for Bug/Incident, sprint placement for Feature/Task/Spike). Ask the user: **"Does this data look correct? Should I proceed with updating the ticket?"** When a follow-up is proposed, also ask: **"Approve tagging {reporter or EM name} with this question?"** When the archetype detection is non-obvious (issue type and content disagree), also ask: **"Detected archetype is {X}; is that right?"** @@ -367,7 +363,7 @@ After approval, branch by `follow_up_needed`: **Applies to:** Bug, Incident. **Skipped on:** Feature, Task, Spike (use Phase 4b instead). `follow_up_needed = true` (use Phase 4c instead). -After the user approved the comment text at Phase 3, post the previewed comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. All comments this agent posts are ADF, never markdown. Logical structure (build it as ADF nodes; the structure below shows the rendered intent, not the source format): +After the user approved the comment text at Phase 3, post the comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. The body uses the prose-style-cleaned draft from Phase 2.5 (the user already saw the cleaned version at the Phase 3 gate). Do not re-draft the comment here. All comments this agent posts are ADF, never markdown. Logical structure (build it as ADF nodes; the structure below shows the rendered intent, not the source format): > **Assessment:** > @@ -401,7 +397,7 @@ Rules: **Applies to:** Feature, Task, Spike. **Skipped on:** Bug, Incident (use Phase 4a instead). `follow_up_needed = true` (use Phase 4c instead). -After the user approved the comment text at Phase 3, post the previewed comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. The comment summarizes what is in scope based on the investigation findings, named in archetype-appropriate terms. +After the user approved the comment text at Phase 3, post the comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. The body uses the prose-style-cleaned draft from Phase 2.5 (the user already saw the cleaned version at the Phase 3 gate). Do not re-draft the comment here. The comment summarizes what is in scope based on the investigation findings, named in archetype-appropriate terms. Logical structure (build it as ADF nodes; the structure below shows the rendered intent): @@ -442,7 +438,7 @@ Rules: Run this phase instead of Phase 4a or 4b when the user approved a follow-up at Phase 3. -1. Confirm you have the approved draft from Phase 3 and the target `accountId` (reporter or EM). +1. Confirm you have the approved (and prose-style-cleaned, from Phase 2.5) draft from Phase 3 and the target `accountId` (reporter or EM). Do not re-draft or re-style the body here. 2. Post the follow-up via `addCommentToJiraIssue` with `contentFormat: "adf"`. The comment body must be a JSON-stringified ADF doc. Never use `contentFormat: "markdown"` or the `[~accountid:XXXXX]` wiki-markup form. Build the ADF with: - A leading paragraph whose first node is a `mention` node (`type: "mention"`, `attrs.id: ""`, `attrs.text: "@"`). - Follow-up paragraphs containing the exact body text the user approved. Inline ticket keys are `text` nodes with `link` marks. Bold uses `marks: [{"type": "strong"}]`. Bullets use `bulletList` → `listItem` → `paragraph` → `text`. @@ -457,7 +453,7 @@ After this phase, continue to Phase 5. ### Phase 5: Refine the Ticket -Invoke the `jira-ticket-refiner` skill via `Skill` to produce the refined description and title. Then apply the `prose-style` skill's writing rules to the output before posting. +This phase runs two skills in sequence. First, invoke `jira-ticket-refiner` via the `Skill` tool to produce the refined title and description. Then invoke `prose-style` via the `Skill` tool, passing the refiner output (title + description), to clean writing-style anti-patterns. Only after both skills run does the user-facing preview appear in step 3 below. **Fallback (when `jira-ticket-refiner` is not installed):** @@ -470,13 +466,14 @@ Invoke the `jira-ticket-refiner` skill via `Skill` to produce the refined descri - **Spike:** Summary, Context and Background, Questions to Answer, Findings (if any). 4. Rewrite the title using `{Area}: {specific problem or goal}` for any archetype, or `{Area} + {Customer}: {specific problem}` for customer-specific bugs, or `P{n}: {Area} {short problem statement}` for incidents, or `Spike: {Area} {question to answer}` for spikes. -**Fallback (when `prose-style` is not installed):** apply at minimum these rules: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists when content flows naturally as sentences. +**Fallback (when `prose-style` is not installed):** apply at minimum these rules to the refined title + description before previewing: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists when content flows naturally as sentences. Steps: -1. Build the refined title and description. -2. Preview the refined title + description to the user as inline markdown (not wrapped in an outer code fence). Get approval. -3. Update via `editJiraIssue` with `contentFormat: "markdown"`. -4. If a "Bug Description" custom field was discoverable in prerequisites, write the same content to that field as raw ADF (`type: "doc"`, `version: 1`) in a separate `editJiraIssue` call. Some Jira instances reject markdown for that field type. If the field doesn't exist, skip this step silently. +1. Build the refined title and description (`jira-ticket-refiner` invocation, or its fallback above). +2. Invoke the `prose-style` skill via the `Skill` tool, passing the refined title and description from step 1 as input. Replace the title and description with the cleaned versions returned by the skill (or run the inline fallback rule list above when the skill does not load). +3. Preview the cleaned refined title + description to the user as inline markdown (not wrapped in an outer code fence). Get approval. +4. Update via `editJiraIssue` with `contentFormat: "markdown"`. +5. If a "Bug Description" custom field was discoverable in prerequisites, write the same content to that field as raw ADF (`type: "doc"`, `version: 1`) in a separate `editJiraIssue` call. Some Jira instances reject markdown for that field type. If the field doesn't exist, skip this step silently. **Preserve all original media, attachments, and links.** Screenshots, videos, recordings, images, and file attachments from the original description must be carried into the refined version. Reproduce them with the same markdown image/link syntax. If the original embeds media you cannot reproduce in markdown, keep the original markup verbatim in that section. Never drop attachments, embedded images, inline links, or any referenced files. @@ -545,7 +542,7 @@ Apply the remaining field updates and the final transition. The field changes (a 1. **Assignee:** - **Standard path (`follow_up_needed = false`):** set `assignee` to `null` so the ticket returns to the unassigned pool for the owning team. - - **Follow-up path (`follow_up_needed = true`):** Phase 4b already assigned the ticket; do not touch the assignee in this phase. + - **Follow-up path (`follow_up_needed = true`):** Phase 4c already assigned the ticket to the tagged person; do not touch the assignee in this phase. Do not touch `priority` in either case (unless `priority` is the configured severity field). 2. **Transition:** by archetype, severity, and path: diff --git a/jira-issue-triage/commands/setup.md b/jira-issue-triage/commands/setup.md index 8720519..3a7b84e 100644 --- a/jira-issue-triage/commands/setup.md +++ b/jira-issue-triage/commands/setup.md @@ -1,6 +1,7 @@ --- description: First-time setup wizard for jira-issue-triage. Walks through configuration questions and writes .claude/jira-issue-triage.config.json. argument-hint: (no args) +allowed-tools: Read, Write, AskUserQuestion, mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources, mcp__plugin_atlassian_atlassian__atlassianUserInfo --- # jira-issue-triage Setup Wizard @@ -13,7 +14,7 @@ Walk the user through eight configuration questions and write the result to `.cl Read `.claude/jira-issue-triage.config.json` if it exists. Also check `.claude/jira-bug-triage.config.json` (the legacy path used by the v0.3.0 plugin). -- **New-path config exists:** Read it, show the current contents to the user as pretty-printed JSON, then ask via `AskQuestion`: "Overwrite the existing config?" Options: `Yes, walk through the wizard again`, `No, keep current and exit`. On "No", exit cleanly. +- **New-path config exists:** Read it, show the current contents to the user as pretty-printed JSON, then ask via `AskUserQuestion`: "Overwrite the existing config?" Options: `Yes, walk through the wizard again`, `No, keep current and exit`. On "No", exit cleanly. - **Only the legacy file exists:** Read it, show the contents, and tell the user: "The legacy file works for now but the new path `.claude/jira-issue-triage.config.json` is preferred. The wizard will write the new path; you can delete the legacy file once the new one is written." Continue to step 2. - **Neither exists:** Continue to step 2. @@ -28,7 +29,7 @@ Best-effort auto-discovery to suggest defaults. Failures are non-fatal; fall bac ### 3. Walk through the eight wizard questions -Ask one at a time. Use `AskQuestion` for multiple-choice answers. Use plain free-text prompts for names, emails, labels, and channel names. Confirm each answer before moving to the next question. +Ask one at a time. Use `AskUserQuestion` for multiple-choice answers. Use plain free-text prompts for names, emails, labels, and channel names. Confirm each answer before moving to the next question. #### Q1: Default project key @@ -42,7 +43,7 @@ Default: `infer`. Validate that the answer is either `infer` or a non-empty alph #### Q2: Severity field name -Use `AskQuestion`: +Use `AskUserQuestion`: > Which Jira field holds the severity for bug tickets? @@ -83,7 +84,7 @@ Three free-text prompts in sequence: #### Q6: Severity scheme -Use `AskQuestion`: +Use `AskUserQuestion`: > Which severity scheme do you want to use? @@ -94,7 +95,7 @@ Options: On "5-tier", use the static 5-tier scheme that strictly extends the 3-tier defaults so users do not get surprise SLA changes when they switch tiers: `Sev-1` (7 days, escalate), `Sev-1.5` (7 days, escalate), `Sev-2` (14 days), `Sev-2.5` (30 days), `Sev-3` (90 days). This matches the 5-tier example in the plugin README. -On "Custom", walk through each level: ask for the level name, the `due_offset_days` integer, and via `AskQuestion` whether `escalate_immediately` is `Yes` or `No`. Loop until the user types `done` for the level name. +On "Custom", walk through each level: ask for the level name, the `due_offset_days` integer, and via `AskUserQuestion` whether `escalate_immediately` is `Yes` or `No`. Loop until the user types `done` for the level name. #### Q7: Escalation @@ -110,7 +111,7 @@ Parse the contact strings into `{ "name": "Alice Kumar", "email": "alice@example #### Q8: Save? -Show the assembled config as pretty-printed JSON with sorted top-level keys. Use `AskQuestion`: +Show the assembled config as pretty-printed JSON with sorted top-level keys. Use `AskUserQuestion`: > Save this config to `.claude/jira-issue-triage.config.json`? diff --git a/jira-issue-triage/skills/prose-style/SKILL.md b/jira-issue-triage/skills/prose-style/SKILL.md new file mode 100644 index 0000000..7cb22ed --- /dev/null +++ b/jira-issue-triage/skills/prose-style/SKILL.md @@ -0,0 +1,71 @@ +--- +name: prose-style +description: 'Rewrites or audits prose to eliminate AI writing patterns and produce natural human writing. Use when the user says "AI slop", "sounds generated", "too formal", "write like a human", "fix the writing", "rewrite this", "clean up the prose", or asks for writing style guidance. Also apply proactively when generating documentation, Slack messages, Confluence pages, Jira ticket text, or any long-form prose output.' +metadata: + author: Taha Bikanerwala +tools: Read +--- + +# Prose Style + +Rewrite or audit text so it reads like a person wrote it. Strip the patterns that signal a model is talking: em dashes used as separators, opener phrases, LLM vocabulary, bullet sprawl where prose would do. The skill never invents content; it changes phrasing, structure, and word choice while every fact and claim from the input survives. + +## Setup + +**Read both reference files before doing anything else.** They contain the complete catalog and calibration examples needed to do this correctly. + +1. Read `references/anti-patterns.md` for the full catalog of patterns to avoid, with corrections. +2. Read `references/examples.md` for before/after pairs that calibrate the difference between AI output and natural prose. + +Do not proceed until both files are loaded. + +## Calling Convention + +The skill works two ways. When a user pastes text and asks for a rewrite or audit, run end to end. When the `jira-issue-triage` agent calls this skill, take the input the agent passes and return the cleaned version. The agent invokes the skill at two points in the workflow: + +| Caller invocation point | Input the skill receives | Return value | +|-------------------------|--------------------------|--------------| +| Phase 2.5 (draft cleaning) | The drafted assessment comment, scope summary, or reporter follow-up question text (still in markdown shape, before ADF construction). | The same text with prose-style rules applied. The agent uses the cleaned text both for the Phase 3 preview shown to the user and as the source of the ADF nodes built in Phase 4a, 4b, or 4c. | +| Phase 5 (refinement cleaning) | The refined title and description produced by `jira-ticket-refiner`. | The same content with prose-style rules applied, ready for the Phase 5 user-facing preview. | + +In both contexts, the skill never posts to Jira on its own. The agent owns the `addCommentToJiraIssue` and `editJiraIssue` calls. + +- **One pass over the input.** Scan, flag, rewrite, verify. No iterative back-and-forth with the user unless asked. +- **Audit mode is read-only.** When the user asks to audit without rewriting, list violations grouped by category, one line each. Do not produce a rewrite. +- **No content invention.** The rewrite preserves every fact and claim from the input. It changes phrasing, structure, and word choice. It does not add or remove substance. + +## Core Principle + +The reader should never suspect a model wrote this. No tells: no em dashes, no spaced hyphens used as separators, no opener phrases, no LLM vocabulary, no bullet lists where prose would do. Say the thing directly. Lead with the answer. Cut everything that does not add meaning. + +## Workflow + +| Step | Action | +|------|--------| +| 1. Scan | Read the full text and identify every anti-pattern instance using the loaded catalog. | +| 2. Flag | Note each violation by category (punctuation, opener, word choice, structure, conciseness). | +| 3. Rewrite | Produce the revised version. Do not explain each change unless asked. | +| 4. Verify | Re-read the output before delivering. Check: (a) any catalog pattern still present, (b) near-synonyms of flagged words introduced (e.g. "solid" for "robust", "frictionless" for "seamlessly"), (c) whether it is terse-and-natural vs. terse-and-abrupt. Fix anything found. | + +When asked to audit without rewriting, list only the violations, grouped by category, one line each. + +## Critical Rules (Always Active) + +- Never use em dashes (—). Restructure the sentence. +- Never use spaced hyphens as separators ( - ). Use a period or rewrite. +- Never open with "Certainly!", "Great question!", "Of course!", or similar. +- Lead with the answer. Reasoning comes after, only if needed. +- No trailing summaries on short responses. +- Terse is not the same as abrupt. Removing filler is correct; removing context the reader needs is not. If the output would feel curt coming from a colleague, add one clause back. + +## Anti-Patterns + +These are hard rules. Each one prevents a failure mode that has been observed on real rewrites. + +1. **Never replace a flagged word with a near-synonym.** Swapping "robust" for "solid" or "leverage" for "harness" defeats the purpose. Describe the thing specifically instead. +2. **Never preserve the opener pattern while changing the words.** Replacing "Certainly!" with "Absolutely!" or "Sure thing!" is the same violation. +3. **Never split prose that flows naturally into bullet points just to add structure.** Two-sentence thoughts split into four single-line bullets lose context and rhythm. +4. **Never add a "to summarize" or "in conclusion" line to a short response.** The reader just read it. +5. **Never hedge a claim that should be stated plainly.** "This may potentially be worth considering in some cases" is a way of avoiding the assertion. Either commit or cut. +6. **Never invent content during the rewrite.** The rewrite changes phrasing, not substance. If a fact is missing, leave it missing. +7. **Never confuse terse with rude.** Removing filler is correct; stripping context a colleague would expect is not. Re-add one clause when the output would land cold. diff --git a/jira-issue-triage/skills/prose-style/references/anti-patterns.md b/jira-issue-triage/skills/prose-style/references/anti-patterns.md new file mode 100644 index 0000000..3f73ba6 --- /dev/null +++ b/jira-issue-triage/skills/prose-style/references/anti-patterns.md @@ -0,0 +1,164 @@ +# Anti-Patterns Reference + +Every pattern here is a tell. When a reader sees these, they know a model wrote it. + +--- + +## Punctuation and Structure + +**Em dash (—)** + +> The system failed — causing a cascade of errors. + +Fix: restructure. "The system failed, which caused a cascade of errors." Or split into two sentences. + +**Spaced hyphen as separator ( - )** + +> The fix is simple - just restart the service. + +Fix: use a period or a comma. "The fix is simple: restart the service." Never use ` - ` as a clause separator. + +**Parenthetical pile-ups** + +> The approach (which has been validated by the team and is consistent with prior art) works well. + +Fix: make it a sentence, or cut it. "The team validated this approach. It works well." + +--- + +## Opener Patterns + +Delete these outright. They add zero meaning and signal immediately that a model is talking. + +- "Certainly!" / "Of course!" / "Absolutely!" / "Sure!" +- "Great question!" +- "I'd be happy to help with that." +- "I'll walk you through..." +- "Let me explain..." +- "I'm glad you asked." + +Also cut soft openers that delay the answer: + +- "It's worth noting that..." (just say it) +- "It's important to understand that..." (just say it) +- "Before we dive in..." (just dive in) +- "To answer your question..." (just answer it) + +--- + +## The LLM Lexicon + +These words appear in AI output at a rate that no human writer would match. When you see them, replace with plain language or cut. + +| Word | Plain alternative | +|------|-------------------| +| delve | look, explore, dig into | +| leverage / harness | use | +| utilize | use | +| robust / solid / bulletproof | strong, reliable (or just describe what it does) | +| streamline | simplify, speed up | +| seamlessly / frictionlessly | smoothly, without friction (or just describe the integration) | +| comprehensive | thorough, complete, full | +| nuanced | specific, subtle, careful | +| elevate | improve, raise | +| foster | build, encourage, support | +| notably / significantly / meaningfully | (cut; vague intensifiers) | +| importantly | (cut; just say the thing) | +| furthermore / moreover | also, and | +| paradigm | model, approach, way of thinking | +| ecosystem | system, environment, tools | +| holistic | whole, full, end-to-end | +| innovative | new, better (or just describe it) | +| cutting-edge | new, recent (or just describe it) | +| synergy | (almost always cut) | +| empower | let, help, enable | +| facilitate | help, make possible | +| considerable / substantial | real, actual, large (or give the number) | +| impactful | useful, effective (or describe the impact specifically) | + +**Near-synonym trap:** replacing a flagged word with its near-equivalent defeats the purpose. Common swaps to watch for: + +- "solid" for "robust" (still a vague confidence signal) +- "frictionless" for "seamlessly" (same pattern, different word) +- "harness" for "leverage" (same verb, same tell) +- "considerable" for "significant" (both are vague intensifiers) +- "meaningful" for "impactful" (both dodge the actual number or effect) + +When you catch yourself reaching for one of these, just describe the thing specifically instead. + +--- + +## Sentence Structure + +**"Here are X things to..."** + +> Here are five ways to improve your API design. + +Fix: just start with the first point, or use a plain statement. "Good API design comes down to a few things." + +**Uniform bullet openers** + +Every bullet starting "This allows...", "This ensures...", "This provides..." reads like a template. Vary the structure or collapse into prose. + +**Passive voice hiding the actor** + +> The issue was identified and resolved. + +Fix: say who did what. "Engineering caught the issue and patched it Thursday." + +**Stacked hedges** + +> This may potentially be worth considering in some cases. + +Fix: commit. "This is worth considering." Or cut the thought entirely if it is not strong enough to assert. + +**Nominalization creep** + +> The implementation of the feature resulted in an improvement of performance. + +Fix: use verbs. "Implementing the feature improved performance." + +--- + +## Conciseness Failures + +**Leading with reasoning instead of the answer** + +> Given the constraints of the system and the fact that the database is under heavy load, and considering that the current caching layer doesn't cover this query path, the answer is to add a cache. + +Fix: "Add a cache. The database is under heavy load and this query path is not covered by the existing layer." + +**Restating what was just said** + +Ending a short response with "In summary..." or "To recap..." when the reader just read it. Cut. + +**Filler transitions** + +- "Moving on..." / "Next up..." / "Now let's look at..." +- "With that said..." / "That being said..." +- "At the end of the day..." + +**Over-qualifying** + +- "very", "quite", "really", "extremely", "incredibly" (cut unless the degree is meaningful) +- "somewhat", "a bit", "kind of" (usually softening a claim that should be stated plainly) + +**Throat-clearing openers** + +> In this section, we will explore the various factors that contribute to the overall performance of the system. + +Fix: just start exploring. Or cut the section header and merge it into the previous section. + +--- + +## Bullet Overuse + +Bullets are for genuinely list-like things: steps, options, items. They are not for prose that happens to have multiple points. + +**When bullets hurt:** + +- A 2-sentence thought split into 4 single-line bullets loses context and rhythm. +- Bullets create artificial parallelism that flattens meaning. +- A response that is 80% bullets reads like a template, not a person. + +**Heuristic:** if you could connect the bullets with "and", "but", or "because" and it reads naturally, write it as prose instead. diff --git a/jira-issue-triage/skills/prose-style/references/examples.md b/jira-issue-triage/skills/prose-style/references/examples.md new file mode 100644 index 0000000..8ceaf89 --- /dev/null +++ b/jira-issue-triage/skills/prose-style/references/examples.md @@ -0,0 +1,93 @@ +# Before/After Examples + +These calibrate the difference between AI output and natural prose. The goal is not formal vs. casual. It is human vs. generated. + +--- + +## Example 1: Short Explanation + +**Before (AI slop)** + +> Certainly! I'd be happy to explain how caching works. It's worth noting that caching is a robust mechanism that can seamlessly improve performance by leveraging stored data to avoid redundant computation. Furthermore, it's important to understand that cache invalidation is one of the more nuanced challenges in software engineering. + +**After** + +> Caching stores the result of an expensive operation so you don't have to repeat it. The hard part is knowing when to throw the cached result away. That is cache invalidation, and it is where most bugs live. + +--- + +## Example 2: Recommendation + +**Before (AI slop)** + +> Based on a comprehensive analysis of the various factors at play, I would recommend that you consider implementing a rate limiter. This would potentially help to streamline the request flow and could possibly reduce the likelihood of downstream service failures. It's also worth noting that this approach is consistent with industry best practices. + +**After** + +> Add a rate limiter. Unthrottled traffic is what is taking down your downstream services, and rate limiting is the standard fix. + +--- + +## Example 3: Slack Message + +**Before (AI slop)** + +> Hi team! I wanted to reach out and let everyone know that the deployment has been completed successfully. I'm happy to report that all systems are functioning as expected, and there were no issues encountered during the process. Please don't hesitate to reach out if you have any questions or concerns! + +**After** + +> Deploy is done. Everything looks healthy. Let me know if you see anything weird. + +--- + +## Example 4: Status Update + +**Before (AI slop)** + +> In this update, I'll walk you through the current state of the project. There are several key areas to highlight: +> +> - The backend API has been implemented and is functioning as expected +> - The frontend integration is currently in progress +> - Testing is ongoing and we anticipate completion by end of week +> - Stakeholder review is scheduled for next week + +**After** + +> Backend API is done. Frontend integration is in progress, testing wraps up Friday, and stakeholder review is next week. + +--- + +## Example 5: Technical Writeup Opening + +**Before (AI slop)** + +> In this document, we will delve into the various aspects of our new authentication system. This comprehensive overview will explore the innovative approach we've taken to foster a more robust and seamless user experience while ensuring that security considerations are holistically addressed. + +**After** + +> This document covers the new authentication system: how it works, why we built it this way, and what you need to know to integrate with it. + +--- + +## Example 6: Jira Ticket Description (plugin-specific) + +**Before (AI slop)** + +> This ticket aims to comprehensively address the issue where users are unable to seamlessly authenticate via the SSO flow. The robust solution we propose would leverage the existing identity provider ecosystem to elevate the user experience. Furthermore, it's worth noting that this approach is consistent with our holistic security paradigm. + +**After** + +> Users cannot complete the SSO sign-in flow. The redirect from the identity provider returns a 401 even with a valid session cookie. We need to fix the cookie domain mismatch in the auth middleware (see linked PR for the working hypothesis). + +--- + +## Calibration Notes + +The after versions share a few things: + +- They open with the point, not a setup. +- They use short sentences and plain words. +- They do not hedge unless the uncertainty is meaningful. +- They treat the reader as an adult who can handle a direct statement. + +Length reduction is a side effect, not the goal. A long piece of writing can be completely human. It just cannot be padded. From dda330be2ba4a00841519077effef96e0b377302 Mon Sep 17 00:00:00 2001 From: TahaBikanerwala Date: Mon, 4 May 2026 16:50:35 +0530 Subject: [PATCH 3/6] feat(jira-issue-triage): split Phase 3 gate; archetype-gated assignment; bump to v1.2.0 Co-Authored-By: Claude Opus 4.7 (1M context) --- jira-issue-triage/.claude-plugin/plugin.json | 2 +- jira-issue-triage/README.md | 14 ++-- jira-issue-triage/agents/jira-issue-triage.md | 75 ++++++++++++------- 3 files changed, 56 insertions(+), 35 deletions(-) diff --git a/jira-issue-triage/.claude-plugin/plugin.json b/jira-issue-triage/.claude-plugin/plugin.json index dec68dd..b5d7c2e 100644 --- a/jira-issue-triage/.claude-plugin/plugin.json +++ b/jira-issue-triage/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jira-issue-triage", - "version": "1.1.0", + "version": "1.2.0", "description": "End-to-end Jira issue triage subagent across all archetypes (Bug, Incident, Feature, Task, Spike). Bundles four skills (issue-investigator for Bug/Incident, requirements-investigator for Feature/Task/Spike, jira-ticket-refiner for any archetype, prose-style for writing-rule application) and ships a /jira-issue-triage:setup wizard.", "author": { "name": "Taha Bikanerwala", diff --git a/jira-issue-triage/README.md b/jira-issue-triage/README.md index 057d354..d172828 100644 --- a/jira-issue-triage/README.md +++ b/jira-issue-triage/README.md @@ -227,7 +227,7 @@ The workflow runs a generic core for every archetype. Five phases gate on the de | Phase 1 | Investigation: `issue-investigator` (Bug/Incident) or `requirements-investigator` (Feature/Task/Spike). | All (skill choice gates on archetype) | | Phase 2 | Datadog log search using signals from Phase 1. Silently suppressed on errors. | Bug, Incident | | Phase 2.5 | Decide whether reporter follow-up is warranted (missing data / clarification / fix verification or relevance check). Form severity recommendation (Bug/Incident) or scope summary (Feature/Task/Spike). Draft the matching Phase 4 comment (assessment, scope summary, or follow-up question) in markdown, then run `prose-style` on it so Phase 3 previews a styled draft. | All | -| Phase 3 | **Hard pause.** Show findings, archetype detection, and proposed updates. Wait for your approval. | All | +| Phase 3 | **Hard pause.** Show findings, archetype detection, and proposed updates. Asks separately whether to post the proposed comment and whether to refine the title and description; you can approve one and skip the other. Metadata writes (severity / sprint / labels / links) and the final transition always run after the gate. | All | | Phase 4a | Convert the Phase 2.5 cleaned draft to ADF and post the severity assessment comment. | Bug, Incident | | Phase 4b | Convert the Phase 2.5 cleaned draft to ADF and post the scope or AC summary comment. Optionally writes to `scope_summary_field_name` if configured. | Feature, Task, Spike | | Phase 4c | Convert the Phase 2.5 cleaned draft to ADF and post the follow-up question tagging reporter or EM. Replaces Phase 4a or 4b. | All (only when follow_up_needed) | @@ -235,7 +235,7 @@ The workflow runs a generic core for every archetype. Five phases gate on the de | Phase 6 | Severity + due date (Bug/Incident) OR optional sprint placement + story points (Feature/Task/Spike). Skipped on follow-up path. | All (behavior gates on archetype) | | Phase 7 | Link related/duplicate tickets. | All | | Phase 8 | Append triaged label. Fill optional fields if discoverable. | All | -| Phase 9 | Final assignee + transition (Backlog for low-severity Bug/Incident, configured ready transition for Feature/Task/Spike if set, Waiting for Reply on follow-up path, otherwise stay in investigating). | All | +| Phase 9 | Final assignee + transition. **Bug unassigns** (returns to the team pool); **Incident, Feature, Task, Spike stay assigned to you** since you'll keep owning the work. Transition: Backlog for low-severity Bug/Incident, configured ready transition for Feature/Task/Spike if set, Waiting for Reply on follow-up path, otherwise stay in investigating. | All (assignment gates on archetype) | | Phase 10 | Slack DM summary. Optional channel/contact escalation per config. | All | ## Limitations @@ -243,7 +243,8 @@ The workflow runs a generic core for every archetype. Five phases gate on the de The agent will never: - Close or resolve a ticket without your approval. - Modify the `priority` field unless `priority` is the configured severity field. -- Post a comment without showing you the text first. +- Post a comment without showing you the text first AND getting an explicit yes at the Phase 3 gate. +- Refine the title or description without an explicit yes at the Phase 3 gate AND a final preview approval at Phase 5. - Tag the reporter or their EM until investigation is exhausted and a specific gap blocks meaningful triage. Reporter contact is a last resort. - Tag anyone other than the reporter or their EM in a follow-up question. - Remove or overwrite reporter-provided information during refinement (only append). @@ -255,10 +256,13 @@ The agent will never: ## FAQ **Q: Can I run the agent on tickets I'm not assigned to?** -A: Yes. Phase 0 assigns the ticket to you as part of triage. +A: Yes. Phase 0 assigns the ticket to you as part of triage. After triage, only Bug archetypes get unassigned (returned to the team pool). Incident, Feature, Task, and Spike tickets stay assigned to you because the running user is typically the owner who will keep the work. **Q: Can I run the agent on a Feature ticket?** -A: Yes. Phase 1 calls `requirements-investigator` instead of `issue-investigator`. Phase 4 posts a scope summary instead of a severity assessment. Phase 6 is skipped (or does sprint placement + story points if `sprint_field_name` and `story_points_field_name` are configured). +A: Yes. Phase 1 calls `requirements-investigator` instead of `issue-investigator`. Phase 4 posts a scope summary instead of a severity assessment. Phase 6 is skipped (or does sprint placement + story points if `sprint_field_name` and `story_points_field_name` are configured). Phase 9 keeps you assigned. + +**Q: Can I run only part of the workflow, e.g., refine the description but skip the comment?** +A: Yes. The Phase 3 confirmation gate asks separately whether to post the proposed comment and whether to refine the title and description. Answer No to either and the agent skips that write while still doing the metadata updates (severity / sprint / labels / links), the final transition, and the Slack DM summary. **Q: Do I need to run `/jira-issue-triage:setup` before the first ticket?** A: Optional. The agent detects missing config on first run and offers three choices: run the setup command, walk through the same wizard inline, or use defaults. diff --git a/jira-issue-triage/agents/jira-issue-triage.md b/jira-issue-triage/agents/jira-issue-triage.md index 9d4d055..44a30cd 100644 --- a/jira-issue-triage/agents/jira-issue-triage.md +++ b/jira-issue-triage/agents/jira-issue-triage.md @@ -1,7 +1,7 @@ --- name: jira-issue-triage description: "Triages a Jira issue end-to-end across all archetypes (Bug, Incident, Feature, Task, Spike): assigns it, transitions to investigating, runs the matching investigation skill, refines the title and description, posts an archetype-appropriate assessment comment, and DMs you a summary. Use when a developer pastes a Jira ticket link and says triage, investigate, pick up, or process." -tools: Skill, Read, Write, Bash, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__editJiraIssue, mcp__plugin_atlassian_atlassian__addCommentToJiraIssue, mcp__plugin_atlassian_atlassian__transitionJiraIssue, mcp__plugin_atlassian_atlassian__getTransitionsForJiraIssue, mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql, mcp__plugin_atlassian_atlassian__searchConfluenceUsingCql, mcp__plugin_atlassian_atlassian__lookupJiraAccountId, mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources, mcp__plugin_atlassian_atlassian__atlassianUserInfo, mcp__plugin_atlassian_atlassian__getJiraIssueTypeMetaWithFields, mcp__plugin_atlassian_atlassian__createIssueLink, mcp__plugin_slack_slack__slack_search_users, mcp__plugin_slack_slack__slack_search_public_and_private, mcp__plugin_slack_slack__slack_read_thread, mcp__plugin_slack_slack__slack_send_message, mcp__plugin_slack_slack__slack_read_user_profile, mcp__datadog__search_datadog_logs +tools: Skill, Read, Write, Bash, AskUserQuestion, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__editJiraIssue, mcp__plugin_atlassian_atlassian__addCommentToJiraIssue, mcp__plugin_atlassian_atlassian__transitionJiraIssue, mcp__plugin_atlassian_atlassian__getTransitionsForJiraIssue, mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql, mcp__plugin_atlassian_atlassian__searchConfluenceUsingCql, mcp__plugin_atlassian_atlassian__lookupJiraAccountId, mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources, mcp__plugin_atlassian_atlassian__atlassianUserInfo, mcp__plugin_atlassian_atlassian__getJiraIssueTypeMetaWithFields, mcp__plugin_atlassian_atlassian__createIssueLink, mcp__plugin_slack_slack__slack_search_users, mcp__plugin_slack_slack__slack_search_public_and_private, mcp__plugin_slack_slack__slack_read_thread, mcp__plugin_slack_slack__slack_send_message, mcp__plugin_slack_slack__slack_read_user_profile, mcp__datadog__search_datadog_logs --- # Jira Issue Triage Agent @@ -76,7 +76,7 @@ Custom field IDs vary across Jira instances. The agent looks them up by name at 1. **Severity field.** If config has `severity_field_name`, use that name. Otherwise try in order: `Severity Level`, `Severity`, `Bug Severity`. Use `getJiraIssueTypeMetaWithFields` to find the field ID. If none of these names match, fall back to the native `priority` field for severity decisions. 2. **Severity options.** Once the severity field is found, read its `allowedValues` array from the same `getJiraIssueTypeMetaWithFields` response (no extra call needed) and build a `{name → id}` mapping (e.g., `{"Sev-1": "10001", "Sev-2": "10002", "Sev-3": "10003"}`). Cache for the session. 3. **Transitions.** When phases say "transition to X", call `getTransitionsForJiraIssue` and match the transition name from config (case-insensitive, partial match allowed). -4. **Optional custom fields.** "Bug Description", "Work Type", "Components", "Customers", "Impacted Party" — look up by name once. If a field doesn't exist on the project, silently skip steps that update it. Never fail because a field is absent. +4. **Optional custom fields.** Look up these names once via `getJiraIssueTypeMetaWithFields`: "Bug Description", "Work Type", "Components", "Customers", "Impacted Party". Also resolve any names supplied through config: `scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`. Cache each `{name → id}` pair found. If a field doesn't exist on the project, silently skip steps that reference it. Never fail because a field is absent. Phase 4b's optional side write to the scope-summary field, and Phase 6's sprint and story-point writes, depend on the matching ID being cached here; if any of those config keys is set but the field isn't found, the agent continues without the side write and notes the miss in the Phase 10 DM. ## Sibling Skills @@ -225,21 +225,20 @@ Follow with the scenario template above. For each ticket the user pastes, execute these phases in order. Pause only at the explicit confirmation gate in Phase 3. If Phase 2.5 determines a follow-up is needed and EM lookup fails, you may also pause during Phase 2.5 to ask the user who to tag. That is the only other allowed pause before Phase 3. -The workflow runs a generic core for every archetype. Phase 1 branches by archetype to call the matching investigation skill. Phases 2 (Datadog), 4 (severity assessment vs scope summary), and 6 (severity + due date vs sprint placement) gate on archetype. +The workflow runs a generic core for every archetype. Phase 1 branches by archetype to call the matching investigation skill. Phases 2 (Datadog), 4 (severity assessment vs scope summary), 6 (severity + due date vs sprint placement), and 9 (Bug unassigns; other archetypes stay assigned to the running user) gate on archetype. --- ### Phase 0: Fetch, Detect Archetype, and Assign 1. Extract the ticket key from the pasted link (e.g., `BUG-12345`). If `project_key` is null in config, infer it from the prefix. -2. Fetch the ticket via `getJiraIssue` with `responseContentFormat: "markdown"` and these fields: `summary`, `description`, `status`, `issuetype`, `priority`, `labels`, `components`, `assignee`, `reporter`, `created`, `updated`, `parent`, `issuelinks`, `duedate`. Add the auto-discovered severity field ID and any optional custom field IDs (Bug Description, Scope Summary, Work Type, Components, Customers, Impacted Party, Sprint, Story Points) found during prerequisite auto-discovery. `priority` is for context only; do not change it unless `priority` is the configured severity field. +2. Fetch the ticket via `getJiraIssue` with `responseContentFormat: "markdown"` and these fields: `summary`, `description`, `comment`, `status`, `issuetype`, `priority`, `labels`, `components`, `assignee`, `reporter`, `created`, `updated`, `parent`, `issuelinks`, `duedate`. Add the auto-discovered severity field ID and any optional custom field IDs (Bug Description, Scope Summary, Work Type, Components, Customers, Impacted Party, Sprint, Story Points) found during prerequisite auto-discovery. `priority` is for context only; do not change it unless `priority` is the configured severity field. The `comment` field returns the full comment thread inline; reuse the cached payload for any later phase that needs to read prior comments. 3. **Skip-label check.** Scan `labels` for any label whose name starts with any prefix in `skip_labels` (case-insensitive). If matched, stop. Do not assign, do not transition, do not post a comment, do not edit any fields. Report this exact form and wait: > `{TICKET-KEY}` already carries a skip label (`{matched-label}`). Skipping triage. Let me know if you want to override and proceed anyway. Continue past this step only on explicit user override. -4. Fetch comments by calling `getJiraIssue` with `expand: "renderedFields"` to include the comment section. -5. **Detect archetype.** Map the issue type field from the fetched ticket to one of: `Bug`, `Incident`, `Feature`, `Task`, `Spike`. Use the table below. If issue type and content disagree (e.g., issue type `Bug` but content is acceptance criteria and a Figma link), trust the content. Cache the archetype string for downstream phase gating. +4. **Detect archetype.** Map the issue type field from the fetched ticket to one of: `Bug`, `Incident`, `Feature`, `Task`, `Spike`. Use the table below. If issue type and content disagree (e.g., issue type `Bug` but content is acceptance criteria and a Figma link), trust the content. Cache the archetype string for downstream phase gating. | Jira issue type | Archetype | |-----------------|-----------| @@ -250,8 +249,8 @@ The workflow runs a generic core for every archetype. Phase 1 branches by archet | Spike, Research, Investigation | Spike | Tickets whose issue type does not match any row default to the closest match by content. When ambiguous, pick `Task` as the safe default. -6. Assign the ticket to the running user via `editJiraIssue` with `fields: { "assignee": { "accountId": "" } }`. Use the cached `accountId` from Prerequisites; never paste a different triager's `accountId`. -7. Transition to the `investigating` transition (default `Under Investigation`): +5. Assign the ticket to the running user via `editJiraIssue` with `fields: { "assignee": { "accountId": "" } }`. Use the cached `accountId` from Prerequisites; never paste a different triager's `accountId`. +6. Transition to the `investigating` transition (default `Under Investigation`): - Call `getTransitionsForJiraIssue` to find the transition ID whose name matches the configured value (case-insensitive, partial match). - Call `transitionJiraIssue` with that transition ID. @@ -347,21 +346,32 @@ Present findings to the user. Show: - The prose-style-cleaned markdown draft of the question comment from Phase 2.5, shown inline as plain markdown. Phase 4c will convert this same text to ADF on post. - What transition will happen (`waiting_reply`), who the ticket will be assigned to (the tagged person), and what will still run (refine, link, label) vs. skipped (the archetype-specific Phase 4 content, severity + due date for Bug/Incident, sprint placement for Feature/Task/Spike). -Ask the user: **"Does this data look correct? Should I proceed with updating the ticket?"** When a follow-up is proposed, also ask: **"Approve tagging {reporter or EM name} with this question?"** When the archetype detection is non-obvious (issue type and content disagree), also ask: **"Detected archetype is {X}; is that right?"** +Ask the user via `AskUserQuestion`. Each write the agent is about to make is its own decision; the user can approve some and skip others. Pose these as separate questions so each gets an explicit yes or no: -Wait for confirmation. If the user requests changes, adjust and re-present. +1. **Does this data look correct?** (Yes / No, request changes.) If No, adjust and re-present before asking the rest. +2. **Post the proposed Phase 4 comment?** (Yes, post / No, skip the comment.) Caches the answer as `approved_post_comment`. +3. **Refine the title and description in Phase 5?** (Yes, refine and update / No, leave the title and description as-is.) Caches the answer as `approved_refine_description`. +4. When `story_points_field_name` is configured AND the archetype is Feature, Task, or Spike AND `follow_up_needed = false`, also ask: **"Story-point estimate for this ticket?"** Free-text numeric input; accept "skip" or empty answer to leave the field blank. Cache the answer as `story_point_estimate` (numeric value or `null`). Phase 6 reads this cache; with no estimate captured here, Phase 6 silently skips the story-point write. +5. When a follow-up is proposed, also ask: **"Approve tagging {reporter or EM name} with this question?"** A No on this question reverts the run to the standard path: drop the cached follow-up scenario and target `accountId`, set `follow_up_needed = false`, then re-draft the standard-path comment per Phase 2.5 step 4 (assessment for Bug/Incident, scope summary for Feature/Task/Spike), run `prose-style` on it, and re-run Phase 3 from the top with the standard-path plan in view. Get fresh approval on questions 1–4 against the new draft. The user must see the standard-path Phase 4 comment, severity recommendation, due date, and any sprint or story-point proposal before any of those writes happen. +6. When the archetype detection is non-obvious (issue type and content disagree), also ask: **"Detected archetype is {X}; is that right?"** If No, take the corrected archetype, redo Phase 2.5 against it, and re-present the gate. -After approval, branch by `follow_up_needed`: -- `follow_up_needed = false`, archetype Bug or Incident: continue to Phase 4a. -- `follow_up_needed = false`, archetype Feature, Task, or Spike: continue to Phase 4b. -- `follow_up_needed = true` (any archetype): jump to Phase 4c. Phases 4a, 4b, and the archetype-specific parts of Phase 6 are skipped; Phases 5, 7, 8, 9, 10 still run with adjustments noted. +Each question is its own `AskUserQuestion` call; do not chain them into one prompt. Wait for every answer before continuing. If the user requests changes to the draft text or the proposed updates, adjust and re-present the affected questions only. + +After approval, branch by `follow_up_needed` and the cached approval flags: +- `follow_up_needed = false`, archetype Bug or Incident: continue to Phase 4a if `approved_post_comment = true`; otherwise skip Phase 4a and go straight to Phase 5. +- `follow_up_needed = false`, archetype Feature, Task, or Spike: continue to Phase 4b if `approved_post_comment = true`; otherwise skip Phase 4b and go straight to Phase 5. +- `follow_up_needed = true` (any archetype): jump to Phase 4c if `approved_post_comment = true` AND question 5 (the tag-approval question) was answered Yes. If question 5 was answered No, the agent has already downgraded the run to the standard path during the gate (drop scenario + accountId, flip `follow_up_needed`, re-draft standard-path comment, re-run Phase 3 with the new plan). At this point the run continues exactly as a fresh `follow_up_needed = false` branch using the new approval flags from the re-run gate. The user has now reviewed the severity / due date / sprint / story-point / final-transition / final-assignee plan that the standard path will execute, so Phase 6 and Phase 9 may proceed. + +Phase 5 honors `approved_refine_description`: when `false`, skip the `jira-ticket-refiner` invocation, the `prose-style` styling pass, the preview, and the `editJiraIssue` write entirely; the title and description on the ticket stay untouched. When `true`, run Phase 5 as written. + +Phases 6, 7, 8, 9, 10 always run regardless of these two flags; the metadata writes (severity / sprint / labels / links) and the final transition + Slack DM are not gated on the comment or description decisions. --- ### Phase 4a: Severity Assessment Comment -**Applies to:** Bug, Incident. -**Skipped on:** Feature, Task, Spike (use Phase 4b instead). `follow_up_needed = true` (use Phase 4c instead). +**Applies to:** Bug, Incident, with `approved_post_comment = true`. +**Skipped on:** Feature, Task, Spike (use Phase 4b instead). `follow_up_needed = true` (use Phase 4c instead). `approved_post_comment = false` (the user opted out of the comment at Phase 3). After the user approved the comment text at Phase 3, post the comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. The body uses the prose-style-cleaned draft from Phase 2.5 (the user already saw the cleaned version at the Phase 3 gate). Do not re-draft the comment here. All comments this agent posts are ADF, never markdown. Logical structure (build it as ADF nodes; the structure below shows the rendered intent, not the source format): @@ -394,8 +404,8 @@ Rules: ### Phase 4b: Scope Summary Comment -**Applies to:** Feature, Task, Spike. -**Skipped on:** Bug, Incident (use Phase 4a instead). `follow_up_needed = true` (use Phase 4c instead). +**Applies to:** Feature, Task, Spike, with `approved_post_comment = true`. +**Skipped on:** Bug, Incident (use Phase 4a instead). `follow_up_needed = true` (use Phase 4c instead). `approved_post_comment = false` (the user opted out of the comment at Phase 3). After the user approved the comment text at Phase 3, post the comment via `addCommentToJiraIssue` with `contentFormat: "adf"`. The body uses the prose-style-cleaned draft from Phase 2.5 (the user already saw the cleaned version at the Phase 3 gate). Do not re-draft the comment here. The comment summarizes what is in scope based on the investigation findings, named in archetype-appropriate terms. @@ -433,8 +443,8 @@ Rules: ### Phase 4c: Post Follow-up Question (Alternative Path) -**Applies to:** any archetype with `follow_up_needed = true`. -**Skipped on:** `follow_up_needed = false` (use Phase 4a or 4b instead). +**Applies to:** any archetype with `follow_up_needed = true` and `approved_post_comment = true`. +**Skipped on:** `follow_up_needed = false` (use Phase 4a or 4b instead). `approved_post_comment = false` (the user declined the follow-up at Phase 3; the run downgraded to the standard path with no follow-up). Run this phase instead of Phase 4a or 4b when the user approved a follow-up at Phase 3. @@ -453,6 +463,8 @@ After this phase, continue to Phase 5. ### Phase 5: Refine the Ticket +**Skipped when `approved_refine_description = false` from the Phase 3 gate.** The user already opted out of editing the title and description, so do not invoke `jira-ticket-refiner`, do not run the `prose-style` styling pass, do not preview, and do not call `editJiraIssue`. Continue to Phase 6. + This phase runs two skills in sequence. First, invoke `jira-ticket-refiner` via the `Skill` tool to produce the refined title and description. Then invoke `prose-style` via the `Skill` tool, passing the refiner output (title + description), to clean writing-style anti-patterns. Only after both skills run does the user-facing preview appear in step 3 below. **Fallback (when `jira-ticket-refiner` is not installed):** @@ -509,7 +521,7 @@ If the severity field is empty on the ticket, write the recommendation cached in 2. If `sprint_field_name` is configured in config: - Look up the active sprint for the configured `project_key` (or the inferred project key from the ticket URL) via `searchJiraIssuesUsingJql` with `sprint in openSprints() AND project = ` to find a representative sprint ID. - Set the ticket's sprint field to the active sprint ID via `editJiraIssue`. Show the user the chosen sprint name and ID before writing. -3. If `story_points_field_name` is configured AND the user provided an estimate during the Phase 3 confirmation gate, write the estimate to the configured field via `editJiraIssue`. +3. If `story_points_field_name` is configured AND `story_point_estimate` was cached at the Phase 3 gate (question 4) as a numeric value, write the estimate to the configured field via `editJiraIssue`. If the user answered "skip" or left the answer blank at Phase 3, leave the field unwritten. If the field name is configured but the field ID was not resolved during Prerequisites auto-discovery, skip the write silently and note the miss in the Phase 10 DM. 4. If neither field is configured, skip Phase 6 silently for non-bug archetypes. Skip this phase entirely when `follow_up_needed = true`. Severity, due date, sprint placement, and story points all wait until the reporter's reply comes in and the ticket is re-triaged. @@ -541,10 +553,11 @@ Use one `editJiraIssue` call when possible. Apply the remaining field updates and the final transition. The field changes (assignee) go in one `editJiraIssue` call; the transition is a separate `transitionJiraIssue` call (after `getTransitionsForJiraIssue` to look up the transition ID). 1. **Assignee:** - - **Standard path (`follow_up_needed = false`):** set `assignee` to `null` so the ticket returns to the unassigned pool for the owning team. - - **Follow-up path (`follow_up_needed = true`):** Phase 4c already assigned the ticket to the tagged person; do not touch the assignee in this phase. + - **Standard path, archetype Bug:** set `assignee` to `null` so the ticket returns to the unassigned pool for the owning team. Bug triage is a routing role; the running user is not picking up the work. + - **Standard path, archetype Incident, Feature, Task, or Spike:** do not touch the assignee. The running user assigned themselves in Phase 0 and stays the owner. Incidents need a named on-call owner; non-bug archetypes are typically picked up by the same person who triaged them. + - **Follow-up path (`follow_up_needed = true`, any archetype):** Phase 4c already assigned the ticket to the tagged person; do not touch the assignee in this phase. - Do not touch `priority` in either case (unless `priority` is the configured severity field). + Do not touch `priority` in any case (unless `priority` is the configured severity field). 2. **Transition:** by archetype, severity, and path: - **Bug/Incident standard path:** if the post-Phase-6 severity is the lowest level in `severity_scheme` (default `Sev-3`), transition to `backlog` (default `Backlog`). All other levels stay in `investigating` for the owning team to pick up promptly. No transition call is needed; the ticket is already in `investigating` from Phase 0. - **Feature/Task/Spike standard path:** if `non_bug_transitions.ready` is configured, transition to that. Otherwise, leave the ticket in `investigating` so the owning team picks it up. @@ -564,16 +577,20 @@ Pick the outcome that matches what you did: | Situation | Message | |-----------|---------| -| Bug/Incident, lowest severity triaged | `Moved to {backlog transition} after triaging` | -| Bug/Incident, higher severity triaged | `Triaged, staying in {investigating transition} ({SevN})` | -| Feature/Task/Spike, no follow-up | `Triaged, staying in {investigating transition} ({Feature, Task, or Spike})` | -| Feature/Task/Spike, sprint placement applied | `Triaged and added to active sprint, staying in {investigating transition}` | -| Feature/Task/Spike, ready transition configured | `Triaged and moved to {non_bug_transitions.ready}` | +| Bug, lowest severity triaged | `Moved to {backlog transition} after triaging, unassigned` | +| Bug, higher severity triaged | `Triaged, staying in {investigating transition} ({SevN}), unassigned` | +| Incident, lowest severity triaged | `Moved to {backlog transition} after triaging, kept assigned to you` | +| Incident, higher severity triaged | `Triaged, staying in {investigating transition} ({SevN}), kept assigned to you` | +| Feature/Task/Spike, no follow-up | `Triaged, staying in {investigating transition} ({Feature, Task, or Spike}), kept assigned to you` | +| Feature/Task/Spike, sprint placement applied | `Triaged and added to active sprint, staying in {investigating transition}, kept assigned to you` | +| Feature/Task/Spike, ready transition configured | `Triaged and moved to {non_bug_transitions.ready}, kept assigned to you` | | Asked reporter for missing data | `Asked reporter for missing info, moved to {waiting_reply transition}` | | Asked reporter for clarification | `Asked reporter to clarify, moved to {waiting_reply transition}` | | Asked reporter to verify fix | `Asked reporter to confirm if still reproducing, moved to {waiting_reply transition}` | | Asked reporter for relevance check (non-bug) | `Asked reporter if still relevant, moved to {waiting_reply transition}` | | Asked EM (reporter deactivated) | `Reporter deactivated, asked EM {name}, moved to {waiting_reply transition}` | +| Comment skipped at Phase 3 | (append) `No comment posted (skipped at confirmation gate)` | +| Description skipped at Phase 3 | (append) `Title and description left as-is (skipped at confirmation gate)` | | Duplicate (only if user explicitly approved closure) | `Closed as duplicate of ORIGINAL-KEY` | | Severity changed | `Changed severity from {SevX} to {SevY}` | | Closed (only if user explicitly approved closure) | `Closed as {resolution}` | From 8e365ef86b98f21d7dbd2a82e2d0be1b174a0a43 Mon Sep 17 00:00:00 2001 From: TahaBikanerwala Date: Mon, 4 May 2026 17:09:30 +0530 Subject: [PATCH 4/6] feat(jira-issue-triage): UX pass on Phase 3 confirmation gate; bump to v1.3.0 Co-Authored-By: Claude Opus 4.7 (1M context) --- jira-issue-triage/.claude-plugin/plugin.json | 2 +- jira-issue-triage/README.md | 24 ++++--- jira-issue-triage/agents/jira-issue-triage.md | 64 ++++++++++++------- .../skills/jira-ticket-refiner/SKILL.md | 2 +- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/jira-issue-triage/.claude-plugin/plugin.json b/jira-issue-triage/.claude-plugin/plugin.json index b5d7c2e..4c46f20 100644 --- a/jira-issue-triage/.claude-plugin/plugin.json +++ b/jira-issue-triage/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jira-issue-triage", - "version": "1.2.0", + "version": "1.3.0", "description": "End-to-end Jira issue triage subagent across all archetypes (Bug, Incident, Feature, Task, Spike). Bundles four skills (issue-investigator for Bug/Incident, requirements-investigator for Feature/Task/Spike, jira-ticket-refiner for any archetype, prose-style for writing-rule application) and ships a /jira-issue-triage:setup wizard.", "author": { "name": "Taha Bikanerwala", diff --git a/jira-issue-triage/README.md b/jira-issue-triage/README.md index d172828..c7d5807 100644 --- a/jira-issue-triage/README.md +++ b/jira-issue-triage/README.md @@ -113,6 +113,13 @@ Configuration is **optional**. The agent uses sensible defaults if no config fil "story_points_field_name": null, "non_bug_transitions": { "ready": null + }, + "archetype_assignment_after_triage": { + "Bug": "unassign", + "Incident": "self", + "Feature": "self", + "Task": "self", + "Spike": "self" } } ``` @@ -203,9 +210,9 @@ Phase 2 is silently skipped. The agent never mentions Datadog in any output. No Optional fields (`Bug Description`, `Scope Summary`, `Work Type`, `Components`, `Customers`, `Impacted Party`, `Sprint`, `Story Points`) are looked up by name. If a field doesn't exist, the agent skips the steps that update it. No configuration needed. -### Advanced configuration (non-bug archetype tuning) +### Advanced configuration (per-archetype tuning) -Four optional fields tune the agent's behavior on Feature, Task, and Spike tickets. They are not asked by the setup wizard; add them by editing the config file directly when you need them. +Five optional fields tune the agent's behavior. They are not asked by the setup wizard; add them by editing the config file directly when you need them. | Field | Purpose | When to set | |-------|---------|-------------| @@ -213,8 +220,9 @@ Four optional fields tune the agent's behavior on Feature, Task, and Spike ticke | `sprint_field_name` | Custom Jira field name (e.g., `Sprint`). When set, Phase 6 (on Feature/Task/Spike tickets) places the ticket into the active sprint of the configured project. | Your team uses sprints and triage should auto-place new tickets into the current sprint. | | `story_points_field_name` | Custom Jira field name (e.g., `Story Points`). When set, the Phase 3 confirmation gate prompts you for a point estimate, and Phase 6 writes it. | Your team estimates non-bug tickets at triage time. | | `non_bug_transitions.ready` | Transition name (e.g., `Ready for Development`). When set, Phase 9 transitions Feature/Task/Spike tickets to this state instead of leaving them in `investigating`. | Your workflow has a distinct "ready to pick up" state for non-bug work. | +| `archetype_assignment_after_triage` | Object mapping each archetype (`Bug`, `Incident`, `Feature`, `Task`, `Spike`) to either `"unassign"` (return to the team pool) or `"self"` (keep assigned to the running user). Defaults: `Bug = "unassign"`, all others `"self"`. | Your team's ownership rule differs from the default. Sev-1 incidents that auto-route to on-call: set `"Incident": "unassign"`. Bug-fix ownership stays with triager: set `"Bug": "self"`. | -When any of these is null (default), the agent skips the corresponding step silently. +When any of these is null (or omitted, in the case of `archetype_assignment_after_triage`), the agent uses the default behavior described above. ## Workflow phases @@ -227,15 +235,15 @@ The workflow runs a generic core for every archetype. Five phases gate on the de | Phase 1 | Investigation: `issue-investigator` (Bug/Incident) or `requirements-investigator` (Feature/Task/Spike). | All (skill choice gates on archetype) | | Phase 2 | Datadog log search using signals from Phase 1. Silently suppressed on errors. | Bug, Incident | | Phase 2.5 | Decide whether reporter follow-up is warranted (missing data / clarification / fix verification or relevance check). Form severity recommendation (Bug/Incident) or scope summary (Feature/Task/Spike). Draft the matching Phase 4 comment (assessment, scope summary, or follow-up question) in markdown, then run `prose-style` on it so Phase 3 previews a styled draft. | All | -| Phase 3 | **Hard pause.** Show findings, archetype detection, and proposed updates. Asks separately whether to post the proposed comment and whether to refine the title and description; you can approve one and skip the other. Metadata writes (severity / sprint / labels / links) and the final transition always run after the gate. | All | +| Phase 3 | **Hard pause.** Show findings, archetype detection, and proposed updates. Asks all decisions side by side in a single `AskUserQuestion` panel: post the proposed comment? refine the title and description? story-point estimate (when configured)? approve the follow-up tag (when applicable)? You can approve some and skip others. Archetype-correction question (when issue type and content disagree) runs as a separate pre-gate so the draft on the main panel matches the corrected archetype. Metadata writes and the final transition always run after the gate. | All | | Phase 4a | Convert the Phase 2.5 cleaned draft to ADF and post the severity assessment comment. | Bug, Incident | | Phase 4b | Convert the Phase 2.5 cleaned draft to ADF and post the scope or AC summary comment. Optionally writes to `scope_summary_field_name` if configured. | Feature, Task, Spike | | Phase 4c | Convert the Phase 2.5 cleaned draft to ADF and post the follow-up question tagging reporter or EM. Replaces Phase 4a or 4b. | All (only when follow_up_needed) | -| Phase 5 | Refine ticket via `jira-ticket-refiner`, then run `prose-style` on the refined title + description, then preview and update. | All | +| Phase 5 | Refine ticket via `jira-ticket-refiner` (with `skip_preview: true` so the skill doesn't run its own gate), then run `prose-style` on the refined title + description, render the cleaned output inline as an informational preview, then write. The render is **not** a second confirmation; Phase 3 already captured your approval to refine. Interrupt within a few seconds to abort if something looks wrong. | All | | Phase 6 | Severity + due date (Bug/Incident) OR optional sprint placement + story points (Feature/Task/Spike). Skipped on follow-up path. | All (behavior gates on archetype) | | Phase 7 | Link related/duplicate tickets. | All | | Phase 8 | Append triaged label. Fill optional fields if discoverable. | All | -| Phase 9 | Final assignee + transition. **Bug unassigns** (returns to the team pool); **Incident, Feature, Task, Spike stay assigned to you** since you'll keep owning the work. Transition: Backlog for low-severity Bug/Incident, configured ready transition for Feature/Task/Spike if set, Waiting for Reply on follow-up path, otherwise stay in investigating. | All (assignment gates on archetype) | +| Phase 9 | Final assignee + transition. Assignment reads `archetype_assignment_after_triage[]` from config: `"unassign"` returns the ticket to the team pool, `"self"` leaves it with you. Defaults: Bug unassigns; Incident, Feature, Task, Spike stay assigned. Transition: Backlog for low-severity Bug/Incident, configured ready transition for Feature/Task/Spike if set, Waiting for Reply on follow-up path, otherwise stay in investigating. | All (assignment configurable per archetype) | | Phase 10 | Slack DM summary. Optional channel/contact escalation per config. | All | ## Limitations @@ -256,10 +264,10 @@ The agent will never: ## FAQ **Q: Can I run the agent on tickets I'm not assigned to?** -A: Yes. Phase 0 assigns the ticket to you as part of triage. After triage, only Bug archetypes get unassigned (returned to the team pool). Incident, Feature, Task, and Spike tickets stay assigned to you because the running user is typically the owner who will keep the work. +A: Yes. Phase 0 assigns the ticket to you as part of triage. After triage, the ticket either stays with you or returns to the team pool based on `archetype_assignment_after_triage[]`. Defaults: Bug unassigns; Incident, Feature, Task, and Spike stay assigned. Override per archetype if your team uses a different ownership rule (see Advanced configuration above). **Q: Can I run the agent on a Feature ticket?** -A: Yes. Phase 1 calls `requirements-investigator` instead of `issue-investigator`. Phase 4 posts a scope summary instead of a severity assessment. Phase 6 is skipped (or does sprint placement + story points if `sprint_field_name` and `story_points_field_name` are configured). Phase 9 keeps you assigned. +A: Yes. Phase 1 calls `requirements-investigator` instead of `issue-investigator`. Phase 4 posts a scope summary instead of a severity assessment. Phase 6 is skipped (or does sprint placement + story points if `sprint_field_name` and `story_points_field_name` are configured). Phase 9 keeps you assigned by default; override `archetype_assignment_after_triage.Feature` to `"unassign"` if your team prefers Feature tickets to return to the team pool after triage. **Q: Can I run only part of the workflow, e.g., refine the description but skip the comment?** A: Yes. The Phase 3 confirmation gate asks separately whether to post the proposed comment and whether to refine the title and description. Answer No to either and the agent skips that write while still doing the metadata updates (severity / sprint / labels / links), the final transition, and the Slack DM summary. diff --git a/jira-issue-triage/agents/jira-issue-triage.md b/jira-issue-triage/agents/jira-issue-triage.md index 44a30cd..ac2a201 100644 --- a/jira-issue-triage/agents/jira-issue-triage.md +++ b/jira-issue-triage/agents/jira-issue-triage.md @@ -61,6 +61,13 @@ The default config (used as the merge target for parsed values, and as-is when t "story_points_field_name": null, "non_bug_transitions": { "ready": null + }, + "archetype_assignment_after_triage": { + "Bug": "unassign", + "Incident": "self", + "Feature": "self", + "Task": "self", + "Spike": "self" } } ``` @@ -69,6 +76,8 @@ When `primary_contact` or `fallback_contact` is set, supply an object with `name The four trailing optional fields (`scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`) are all null by default. When null, the agent skips the steps that reference them. Documented in the plugin README's Advanced Configuration section. +`archetype_assignment_after_triage` controls Phase 9's assignee behavior per archetype. Valid values per archetype: `"unassign"` (return to the team pool by setting `assignee` to null) or `"self"` (leave the running user assigned, since Phase 0 already assigned them). The defaults match the 1.2.0 behavior: Bug routes back to the pool; Incident, Feature, Task, and Spike stay with the triager. Override per archetype when your team uses a different rule (for example, Sev-1 incidents auto-routing to on-call: set `"Incident": "unassign"` and have your on-call rotation pick the ticket up). + ### Auto-Discovery Custom field IDs vary across Jira instances. The agent looks them up by name at runtime instead of hardcoding. @@ -225,7 +234,7 @@ Follow with the scenario template above. For each ticket the user pastes, execute these phases in order. Pause only at the explicit confirmation gate in Phase 3. If Phase 2.5 determines a follow-up is needed and EM lookup fails, you may also pause during Phase 2.5 to ask the user who to tag. That is the only other allowed pause before Phase 3. -The workflow runs a generic core for every archetype. Phase 1 branches by archetype to call the matching investigation skill. Phases 2 (Datadog), 4 (severity assessment vs scope summary), 6 (severity + due date vs sprint placement), and 9 (Bug unassigns; other archetypes stay assigned to the running user) gate on archetype. +The workflow runs a generic core for every archetype. Phase 1 branches by archetype to call the matching investigation skill. Phases 2 (Datadog), 4 (severity assessment vs scope summary), 6 (severity + due date vs sprint placement), and 9 (assignment per `archetype_assignment_after_triage` config; Bug defaults to unassign, others to self) gate on archetype. --- @@ -346,16 +355,25 @@ Present findings to the user. Show: - The prose-style-cleaned markdown draft of the question comment from Phase 2.5, shown inline as plain markdown. Phase 4c will convert this same text to ADF on post. - What transition will happen (`waiting_reply`), who the ticket will be assigned to (the tagged person), and what will still run (refine, link, label) vs. skipped (the archetype-specific Phase 4 content, severity + due date for Bug/Incident, sprint placement for Feature/Task/Spike). -Ask the user via `AskUserQuestion`. Each write the agent is about to make is its own decision; the user can approve some and skip others. Pose these as separate questions so each gets an explicit yes or no: +Ask the user via `AskUserQuestion`. The decisions are independent (each gates a different write), so put them in one panel as a multi-question call. `AskUserQuestion` accepts up to 4 questions per panel; pick the ones that apply to this run and batch them into a single call so the user sees all the decisions side by side instead of clicking through sequential modals. + +**Pre-gate (separate call, only when applicable).** Run BEFORE the main panel: + +- **When the archetype detection is non-obvious** (issue type and content disagree): ask **"Detected archetype is {X}; is that right?"** as a standalone `AskUserQuestion` call. If the user picks a different archetype, redo Phase 2.5 against the correction and re-enter Phase 3. The pre-gate runs first because the archetype changes the draft content for the main panel. + +**Main panel (one `AskUserQuestion` call with up to 4 questions in `questions[]`).** Always include questions 1 and 2; include 3 and 4 only when their preconditions hold. If all four apply, the panel has 4 side-by-side questions. If only the standard path applies, the panel has 2. -1. **Does this data look correct?** (Yes / No, request changes.) If No, adjust and re-present before asking the rest. -2. **Post the proposed Phase 4 comment?** (Yes, post / No, skip the comment.) Caches the answer as `approved_post_comment`. -3. **Refine the title and description in Phase 5?** (Yes, refine and update / No, leave the title and description as-is.) Caches the answer as `approved_refine_description`. -4. When `story_points_field_name` is configured AND the archetype is Feature, Task, or Spike AND `follow_up_needed = false`, also ask: **"Story-point estimate for this ticket?"** Free-text numeric input; accept "skip" or empty answer to leave the field blank. Cache the answer as `story_point_estimate` (numeric value or `null`). Phase 6 reads this cache; with no estimate captured here, Phase 6 silently skips the story-point write. -5. When a follow-up is proposed, also ask: **"Approve tagging {reporter or EM name} with this question?"** A No on this question reverts the run to the standard path: drop the cached follow-up scenario and target `accountId`, set `follow_up_needed = false`, then re-draft the standard-path comment per Phase 2.5 step 4 (assessment for Bug/Incident, scope summary for Feature/Task/Spike), run `prose-style` on it, and re-run Phase 3 from the top with the standard-path plan in view. Get fresh approval on questions 1–4 against the new draft. The user must see the standard-path Phase 4 comment, severity recommendation, due date, and any sprint or story-point proposal before any of those writes happen. -6. When the archetype detection is non-obvious (issue type and content disagree), also ask: **"Detected archetype is {X}; is that right?"** If No, take the corrected archetype, redo Phase 2.5 against it, and re-present the gate. +1. **Post the proposed Phase 4 comment?** Options: `Yes, post it`, `No, skip the comment`. The "Other" channel lets the user say "post it after these edits: ...". Cache the answer as `approved_post_comment` (boolean) and any free-text feedback as `comment_change_request`. +2. **Refine the title and description?** Options: `Yes, refine and write`, `No, leave as-is`. The "Other" channel lets the user say "refine but skip the title" or similar. Cache the answer as `approved_refine_description` (boolean) and any free-text as `refine_change_request`. The user is approving the refinement up front; Phase 5 will render the cleaned output inline before writing but does not ask again. If the user wants a second checkpoint they say so via "Other" here ("yes refine but show me before writing"). +3. **(Conditional)** When `story_points_field_name` is configured AND the archetype is Feature, Task, or Spike AND `follow_up_needed = false`: **"Story-point estimate?"** Options: `1`, `2`, `3`, `5`, `8`, `13`, `Skip` (a Fibonacci-ish set covers most teams; "Other" accepts any number). Cache the numeric answer as `story_point_estimate` or `null` on Skip. Phase 6 reads this cache; if no estimate was captured, Phase 6 silently skips the story-point write. +4. **(Conditional)** When a follow-up is proposed: **"Approve tagging {reporter or EM name} with this question?"** Options: `Yes, tag {name}`, `No, switch to standard path`. Cache as `approved_followup_tag`. -Each question is its own `AskUserQuestion` call; do not chain them into one prompt. Wait for every answer before continuing. If the user requests changes to the draft text or the proposed updates, adjust and re-present the affected questions only. +If the user feedback (the free-text "Other" channels on questions 1 and 2) requests changes, adjust the affected draft, regenerate prose-style, and re-present the same panel. Loop until the user's free-text channels are empty or the user explicitly approves the latest draft. + +**After the main panel returns:** + +- If `approved_followup_tag = false` (the user said No to tagging): the run downgrades to the standard path. Drop the cached follow-up scenario and target `accountId`, flip `follow_up_needed = false`, re-draft the standard-path comment per Phase 2.5 step 4, run `prose-style` on it, and re-enter Phase 3 with the standard-path plan in view. Build a fresh main panel against the new draft. The user must see the standard-path Phase 4 comment, severity recommendation, due date, and any sprint or story-point proposal before any of those writes happen. +- Otherwise the gate is closed and the run continues with the cached flags. After approval, branch by `follow_up_needed` and the cached approval flags: - `follow_up_needed = false`, archetype Bug or Incident: continue to Phase 4a if `approved_post_comment = true`; otherwise skip Phase 4a and go straight to Phase 5. @@ -481,9 +499,9 @@ This phase runs two skills in sequence. First, invoke `jira-ticket-refiner` via **Fallback (when `prose-style` is not installed):** apply at minimum these rules to the refined title + description before previewing: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists when content flows naturally as sentences. Steps: -1. Build the refined title and description (`jira-ticket-refiner` invocation, or its fallback above). +1. Build the refined title and description (`jira-ticket-refiner` invocation, or its fallback above). When invoking the skill from this phase, pass `skip_preview: true` in the calling context so the skill's own Step 7 preview-and-confirm is suppressed; the agent owns the user-facing surface for the run, and the skill should treat the agent-owned approval at Phase 3 as the gate. 2. Invoke the `prose-style` skill via the `Skill` tool, passing the refined title and description from step 1 as input. Replace the title and description with the cleaned versions returned by the skill (or run the inline fallback rule list above when the skill does not load). -3. Preview the cleaned refined title + description to the user as inline markdown (not wrapped in an outer code fence). Get approval. +3. Render the cleaned refined title + description to the user as inline markdown (not wrapped in an outer code fence). This is **informational, not a question** — Phase 3 already captured the user's approval to refine. The render gives the user a chance to interrupt (Ctrl+C) if something looks egregiously wrong before the write happens. Do not call `AskUserQuestion` here. Frame the output with one line above the render: "Writing the following to `{TICKET-KEY}` (interrupt within a few seconds to abort):". After rendering, proceed immediately to step 4. 4. Update via `editJiraIssue` with `contentFormat: "markdown"`. 5. If a "Bug Description" custom field was discoverable in prerequisites, write the same content to that field as raw ADF (`type: "doc"`, `version: 1`) in a separate `editJiraIssue` call. Some Jira instances reject markdown for that field type. If the field doesn't exist, skip this step silently. @@ -552,12 +570,14 @@ Use one `editJiraIssue` call when possible. Apply the remaining field updates and the final transition. The field changes (assignee) go in one `editJiraIssue` call; the transition is a separate `transitionJiraIssue` call (after `getTransitionsForJiraIssue` to look up the transition ID). -1. **Assignee:** - - **Standard path, archetype Bug:** set `assignee` to `null` so the ticket returns to the unassigned pool for the owning team. Bug triage is a routing role; the running user is not picking up the work. - - **Standard path, archetype Incident, Feature, Task, or Spike:** do not touch the assignee. The running user assigned themselves in Phase 0 and stays the owner. Incidents need a named on-call owner; non-bug archetypes are typically picked up by the same person who triaged them. - - **Follow-up path (`follow_up_needed = true`, any archetype):** Phase 4c already assigned the ticket to the tagged person; do not touch the assignee in this phase. +1. **Assignee:** read the rule from `archetype_assignment_after_triage[]` in the resolved config. Default rules: `Bug = "unassign"`, `Incident = "self"`, `Feature = "self"`, `Task = "self"`, `Spike = "self"`. Apply the rule: + - **Standard path, rule = `"unassign"`:** set `assignee` to `null` via `editJiraIssue` so the ticket returns to the unassigned pool for the owning team. Cache the assignment outcome for Phase 10 as `unassigned`. + - **Standard path, rule = `"self"`:** do not touch the assignee. The running user assigned themselves in Phase 0 and stays the owner. Cache the assignment outcome for Phase 10 as `kept assigned to you`. + - **Follow-up path (`follow_up_needed = true`, any archetype):** Phase 4c already assigned the ticket to the tagged person; do not touch the assignee in this phase. The Phase 10 outcome row covers the follow-up case directly and does not read the cached assignment outcome. Do not touch `priority` in any case (unless `priority` is the configured severity field). + + The default values match the 1.2.0 behavior: Bug routes back to the team pool (bug triage is a routing role); Incident, Feature, Task, and Spike stay with the triager (typical owner for those archetypes). Teams whose Sev-1 incidents auto-route to on-call should set `"Incident": "unassign"` in their config and rely on their on-call rotation to pick up the unassigned ticket. Teams that want bug-fix ownership to stay with the triager should set `"Bug": "self"`. 2. **Transition:** by archetype, severity, and path: - **Bug/Incident standard path:** if the post-Phase-6 severity is the lowest level in `severity_scheme` (default `Sev-3`), transition to `backlog` (default `Backlog`). All other levels stay in `investigating` for the owning team to pick up promptly. No transition call is needed; the ticket is already in `investigating` from Phase 0. - **Feature/Task/Spike standard path:** if `non_bug_transitions.ready` is configured, transition to that. Otherwise, leave the ticket in `investigating` so the owning team picks it up. @@ -577,13 +597,11 @@ Pick the outcome that matches what you did: | Situation | Message | |-----------|---------| -| Bug, lowest severity triaged | `Moved to {backlog transition} after triaging, unassigned` | -| Bug, higher severity triaged | `Triaged, staying in {investigating transition} ({SevN}), unassigned` | -| Incident, lowest severity triaged | `Moved to {backlog transition} after triaging, kept assigned to you` | -| Incident, higher severity triaged | `Triaged, staying in {investigating transition} ({SevN}), kept assigned to you` | -| Feature/Task/Spike, no follow-up | `Triaged, staying in {investigating transition} ({Feature, Task, or Spike}), kept assigned to you` | -| Feature/Task/Spike, sprint placement applied | `Triaged and added to active sprint, staying in {investigating transition}, kept assigned to you` | -| Feature/Task/Spike, ready transition configured | `Triaged and moved to {non_bug_transitions.ready}, kept assigned to you` | +| Bug/Incident, lowest severity triaged | `Moved to {backlog transition} after triaging, {assignment outcome}` | +| Bug/Incident, higher severity triaged | `Triaged, staying in {investigating transition} ({SevN}), {assignment outcome}` | +| Feature/Task/Spike, no follow-up | `Triaged, staying in {investigating transition} ({Feature, Task, or Spike}), {assignment outcome}` | +| Feature/Task/Spike, sprint placement applied | `Triaged and added to active sprint, staying in {investigating transition}, {assignment outcome}` | +| Feature/Task/Spike, ready transition configured | `Triaged and moved to {non_bug_transitions.ready}, {assignment outcome}` | | Asked reporter for missing data | `Asked reporter for missing info, moved to {waiting_reply transition}` | | Asked reporter for clarification | `Asked reporter to clarify, moved to {waiting_reply transition}` | | Asked reporter to verify fix | `Asked reporter to confirm if still reproducing, moved to {waiting_reply transition}` | @@ -598,6 +616,8 @@ Pick the outcome that matches what you did: The "Severity changed" line should only appear when the severity field was updated. Never mention `priority` unless it was the configured severity field. Combine multiple outcomes on one line when they apply (e.g., `Changed severity from Sev-2 to Sev-3. Moved to Backlog after triaging`). +`{assignment outcome}` resolves to the value cached at Phase 9 step 1 (`unassigned` when the archetype rule was `"unassign"`, `kept assigned to you` when the rule was `"self"`). On the follow-up path the assignment outcome is implicit in the "Asked reporter / Asked EM" rows and the placeholder is not used. + **Escalation routing.** If the recommendation's level is marked `escalate_immediately: true` in `severity_scheme`: - If `escalation.slack_channel` is set, send a second message to that channel with the format shown below for Phase 10 templates, and include the cached Slack mention for `primary_contact` (resolved from the configured email at session start) if `primary_contact` is set. diff --git a/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md b/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md index 4528f05..81b3f77 100644 --- a/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md +++ b/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md @@ -16,7 +16,7 @@ This skill modifies Jira. It updates the ticket's description and title (the `fi The skill works two ways. When a user pastes a ticket key and asks to refine it, run end to end. When the `jira-issue-triage` agent calls this skill in Phase 5, treat the agent's already-fetched payload and investigation findings as the source data and skip the fetch step. -- **One confirmation gate.** Always preview the rewrite before calling `editJiraIssue`. The user must approve. +- **One confirmation gate.** Always preview the rewrite before calling `editJiraIssue`. The user must approve. **Exception:** when the calling context passes `skip_preview: true` (the `jira-issue-triage` agent does this in Phase 5 because the agent already captured user approval at Phase 3 and renders its own informational preview before writing), produce the refined title and description, hand them back to the caller, and skip Step 7's preview-and-`editJiraIssue` entirely. The caller owns the write in that mode. - **Read-then-write.** Refuse to write before reading the description, comments, and (when relevant) changelog. - **Strict superset.** The refined ticket contains every fact from the original. Restructure, rewrite, and re-tag, never truncate. - **No solution prescription.** The skill structures information. It does not invent fixes, recommend roadmap, or editorialize on causes that are not in the ticket. From 216a513d3fccf3aae4734cefd7e6df865251e088 Mon Sep 17 00:00:00 2001 From: TahaBikanerwala Date: Tue, 5 May 2026 15:04:54 +0530 Subject: [PATCH 5/6] fix(jira-issue-triage): triage-ux-pass review fixes Co-Authored-By: Claude Opus 4.7 (1M context) --- jira-issue-triage/README.md | 20 ++- jira-issue-triage/agents/jira-issue-triage.md | 162 +++++++++++++++--- jira-issue-triage/commands/setup.md | 14 +- .../skills/jira-ticket-refiner/SKILL.md | 12 +- 4 files changed, 171 insertions(+), 37 deletions(-) diff --git a/jira-issue-triage/README.md b/jira-issue-triage/README.md index c7d5807..94a70ef 100644 --- a/jira-issue-triage/README.md +++ b/jira-issue-triage/README.md @@ -120,7 +120,8 @@ Configuration is **optional**. The agent uses sensible defaults if no config fil "Feature": "self", "Task": "self", "Spike": "self" - } + }, + "description_preview_pause_seconds": 3 } ``` @@ -212,7 +213,7 @@ Optional fields (`Bug Description`, `Scope Summary`, `Work Type`, `Components`, ### Advanced configuration (per-archetype tuning) -Five optional fields tune the agent's behavior. They are not asked by the setup wizard; add them by editing the config file directly when you need them. +Six optional fields tune the agent's behavior. The setup wizard writes them into the saved JSON with their default values so the file is a complete, browsable config; override them by editing the file directly when you want different behavior. | Field | Purpose | When to set | |-------|---------|-------------| @@ -220,9 +221,16 @@ Five optional fields tune the agent's behavior. They are not asked by the setup | `sprint_field_name` | Custom Jira field name (e.g., `Sprint`). When set, Phase 6 (on Feature/Task/Spike tickets) places the ticket into the active sprint of the configured project. | Your team uses sprints and triage should auto-place new tickets into the current sprint. | | `story_points_field_name` | Custom Jira field name (e.g., `Story Points`). When set, the Phase 3 confirmation gate prompts you for a point estimate, and Phase 6 writes it. | Your team estimates non-bug tickets at triage time. | | `non_bug_transitions.ready` | Transition name (e.g., `Ready for Development`). When set, Phase 9 transitions Feature/Task/Spike tickets to this state instead of leaving them in `investigating`. | Your workflow has a distinct "ready to pick up" state for non-bug work. | -| `archetype_assignment_after_triage` | Object mapping each archetype (`Bug`, `Incident`, `Feature`, `Task`, `Spike`) to either `"unassign"` (return to the team pool) or `"self"` (keep assigned to the running user). Defaults: `Bug = "unassign"`, all others `"self"`. | Your team's ownership rule differs from the default. Sev-1 incidents that auto-route to on-call: set `"Incident": "unassign"`. Bug-fix ownership stays with triager: set `"Bug": "self"`. | +| `archetype_assignment_after_triage` | Object mapping each archetype (`Bug`, `Incident`, `Feature`, `Task`, `Spike`) to either `"unassign"` (return to the team pool) or `"self"` (keep assigned to the running user). Defaults: `Bug = "unassign"`, all others `"self"`. Missing keys are filled from defaults. Validation runs at session start (Phase 0); warnings about invalid values are surfaced once in the Phase 10 Slack DM, not as inline output. See the Validation block below. | Your team's ownership rule differs from the default. Sev-1 incidents that auto-route to on-call: set `"Incident": "unassign"`. Bug-fix ownership stays with triager: set `"Bug": "self"`. | +| `description_preview_pause_seconds` | Integer seconds to pause between the Phase 5 informational preview and the `editJiraIssue` write. Default `3`. Set higher (`5`-`10`) if you want more time to read the preview before the write commits. Set to `0` to write immediately (not recommended). | Your team wants more time to skim the rendered description before it lands on the ticket. | + +**Backwards compatibility.** When any of these is null, the agent uses the default behavior described above. The two keys with non-null defaults (`archetype_assignment_after_triage`, `description_preview_pause_seconds`) also backfill on omission: existing 1.2.0 configs that don't include either key get `archetype_assignment_after_triage = {Bug: "unassign", Incident: "self", Feature: "self", Task: "self", Spike: "self"}` and `description_preview_pause_seconds = 3` applied at runtime. No migration steps required; the saved JSON does not need to be edited to upgrade. + +**Validation.** The agent normalizes invalid values at session start and warns once via the Phase 10 DM rather than failing the run: -When any of these is null (or omitted, in the case of `archetype_assignment_after_triage`), the agent uses the default behavior described above. +- `description_preview_pause_seconds`: must be a non-negative integer. Negative, float, string, or null falls back to `3`. +- `archetype_assignment_after_triage`: must be an object whose values are `"unassign"` or `"self"`. A non-object value (string, array, null) is treated as omitted and the full default object applies. Per-key invalid values warn and use the archetype default. Unknown archetype keys (typos like `"Bg"`) are ignored with a warning. +- `scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`: must be strings or null. Non-string values are treated as null with a warning, and the steps that reference them are skipped. ## Workflow phases @@ -239,7 +247,7 @@ The workflow runs a generic core for every archetype. Five phases gate on the de | Phase 4a | Convert the Phase 2.5 cleaned draft to ADF and post the severity assessment comment. | Bug, Incident | | Phase 4b | Convert the Phase 2.5 cleaned draft to ADF and post the scope or AC summary comment. Optionally writes to `scope_summary_field_name` if configured. | Feature, Task, Spike | | Phase 4c | Convert the Phase 2.5 cleaned draft to ADF and post the follow-up question tagging reporter or EM. Replaces Phase 4a or 4b. | All (only when follow_up_needed) | -| Phase 5 | Refine ticket via `jira-ticket-refiner` (with `skip_preview: true` so the skill doesn't run its own gate), then run `prose-style` on the refined title + description, render the cleaned output inline as an informational preview, then write. The render is **not** a second confirmation; Phase 3 already captured your approval to refine. Interrupt within a few seconds to abort if something looks wrong. | All | +| Phase 5 | Refine ticket via `jira-ticket-refiner` (the agent passes `Calling context: skip_preview=true.` as the leading line of the skill prompt so the skill's own preview-and-write gate is suppressed), then run `prose-style` on the refined title + description, render the cleaned output inline as an informational preview, then write after a `description_preview_pause_seconds` pause (default 3). The render is **not** a second confirmation; Phase 3 already captured your approval to refine. Interrupt during the pause to abort if something looks wrong. | All | | Phase 6 | Severity + due date (Bug/Incident) OR optional sprint placement + story points (Feature/Task/Spike). Skipped on follow-up path. | All (behavior gates on archetype) | | Phase 7 | Link related/duplicate tickets. | All | | Phase 8 | Append triaged label. Fill optional fields if discoverable. | All | @@ -252,7 +260,7 @@ The agent will never: - Close or resolve a ticket without your approval. - Modify the `priority` field unless `priority` is the configured severity field. - Post a comment without showing you the text first AND getting an explicit yes at the Phase 3 gate. -- Refine the title or description without an explicit yes at the Phase 3 gate AND a final preview approval at Phase 5. +- Refine the title or description without an explicit yes at the Phase 3 gate. Phase 5 then renders the cleaned output inline as an informational preview and pauses for `description_preview_pause_seconds` (default 3) before writing, so you have a chance to interrupt if something looks wrong; users who want a second confirmation prompt can opt in per run via the "Other" channel on Phase 3 question 2. - Tag the reporter or their EM until investigation is exhausted and a specific gap blocks meaningful triage. Reporter contact is a last resort. - Tag anyone other than the reporter or their EM in a follow-up question. - Remove or overwrite reporter-provided information during refinement (only append). diff --git a/jira-issue-triage/agents/jira-issue-triage.md b/jira-issue-triage/agents/jira-issue-triage.md index ac2a201..db973a8 100644 --- a/jira-issue-triage/agents/jira-issue-triage.md +++ b/jira-issue-triage/agents/jira-issue-triage.md @@ -68,16 +68,37 @@ The default config (used as the merge target for parsed values, and as-is when t "Feature": "self", "Task": "self", "Spike": "self" - } + }, + "description_preview_pause_seconds": 3 } ``` When `primary_contact` or `fallback_contact` is set, supply an object with `name` and `email`: e.g., `{ "name": "Alice Kumar", "email": "alice@example.com" }`. The agent resolves Jira `accountId` (via `lookupJiraAccountId` using the email) and Slack `user_id` (via `slack_search_users` using the email) once per session and caches both. `slack_channel` is a string like `#bug-triage`. -The four trailing optional fields (`scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`) are all null by default. When null, the agent skips the steps that reference them. Documented in the plugin README's Advanced Configuration section. +Six optional fields tune the agent's behavior. Each is documented in the plugin README's Advanced Configuration section. Their defaults: + +| Field | Default when omitted | Default when explicitly `null` | Default on invalid value | +|-------|----------------------|--------------------------------|--------------------------| +| `scope_summary_field_name` | `null` (Phase 4b skips the side write) | `null` | treat non-string as `null` and emit a deferred warning | +| `sprint_field_name` | `null` (Phase 6 skips the sprint write) | `null` | treat non-string as `null` and emit a deferred warning | +| `story_points_field_name` | `null` (Phase 3 omits the story-points question; Phase 6 skips the write) | `null` | treat non-string as `null` and emit a deferred warning | +| `non_bug_transitions.ready` | `null` (Phase 9 leaves Feature/Task/Spike in `investigating`) | `null` | treat non-string as `null` and emit a deferred warning | +| `archetype_assignment_after_triage` | full default object: `{Bug: "unassign", Incident: "self", Feature: "self", Task: "self", Spike: "self"}` | same as omitted (treat explicit `null` or non-object as omitted and emit a deferred warning) | per-key validation per the merge rules below | +| `description_preview_pause_seconds` | `3` | `3` (treat explicit `null` as default and emit a deferred warning) | `3` (treat string, negative, non-integer numeric, or anything else non-conforming as default and emit a deferred warning). Valid values are non-negative integers. | + +The two fields with non-null defaults (`archetype_assignment_after_triage` and `description_preview_pause_seconds`) backfill on upgrade: existing 1.2.0 configs that omit either key inherit the table's default at runtime, so the saved JSON does not need to be edited to upgrade cleanly. + +**Where validation runs and where warnings surface.** Phase 0's auto-discovery applies the validation rules in the third column once per session. Validation never aborts the run; it always normalizes to a working default. The "deferred warning" phrase in the table means: collect the warning in a session-scoped list at Phase 0 and surface it as an appended line on the Phase 10 Slack DM (one line per invalid field). The agent does not print warnings inline at Phase 0 because the user is not yet engaged with the run output at that point; routing them to the closing DM keeps the warnings visible without interrupting the workflow. The `archetype_assignment_after_triage` per-key merge and validation rules are spelled out under "Config merge and validation rules" later in this section. `archetype_assignment_after_triage` controls Phase 9's assignee behavior per archetype. Valid values per archetype: `"unassign"` (return to the team pool by setting `assignee` to null) or `"self"` (leave the running user assigned, since Phase 0 already assigned them). The defaults match the 1.2.0 behavior: Bug routes back to the pool; Incident, Feature, Task, and Spike stay with the triager. Override per archetype when your team uses a different rule (for example, Sev-1 incidents auto-routing to on-call: set `"Incident": "unassign"` and have your on-call rotation pick the ticket up). +**Config merge and validation rules:** + +- **Missing keys.** When `archetype_assignment_after_triage` is omitted entirely, the full default object (Bug → unassign, others → self) applies. When the object is present but missing some archetype keys, fill the missing keys from defaults. The user does not need to list every archetype to override one. +- **Unknown values.** When a value is anything other than `"unassign"` or `"self"` (typo, future-version value the agent doesn't understand, wrong type), record a deferred warning (surfaced in the Phase 10 DM) with the offending archetype name and value, then fall back to the default for that archetype. Do not abort the run; bad config in one slot should not block triage. +- **Unknown archetype keys.** When the object carries a key that is not one of the five archetypes (typo like `"Bg"` or `"Outage"` from a future-version mapping), warn once and ignore that key. Do not raise an error. +- **Future values.** The agent treats any value other than `"unassign"` or `"self"` as unknown (per the rule above). When a future plugin version adds a new value (e.g., `"oncall"` resolved via Slack), 1.3.0 installs gracefully fall back to the default and warn rather than crashing. + ### Auto-Discovery Custom field IDs vary across Jira instances. The agent looks them up by name at runtime instead of hardcoding. @@ -102,6 +123,46 @@ The agent invokes other skills during the workflow. Reference them by name; the All four bundled skills install with the plugin. The defensive fallbacks below fire only on rare runtime load failures; they are not the expected execution path and never need user attention in normal operation. +### Skill calling-context conventions + +When the agent invokes a skill via the `Skill` tool, it can pass instructions to the skill by including a leading `Calling context:` line in the prompt. The convention: + +- The first line of the agent's prompt to the skill is **only** the directive: `Calling context: =[, =...].` (terminated by a period). +- The directive line carries no free-text guidance. Any human-readable instructions, payload data, or skill input go on subsequent lines after a blank line. +- The skill body parses the first line, recognizes known keys, and interprets them. Free-text guidance and payload data on later lines are read normally. +- Unknown keys are ignored by the skill. This is forwards-compat for new flags. + +Currently defined keys: + +| Key | Value | Recognized by | Effect | +|-----|-------|---------------|--------| +| `skip_preview` | `true` / `false` | `jira-ticket-refiner` (Phase 5) | When `true`, skill skips its Step 7 preview-and-write; returns the refined title and description as plain text for the agent to write. | + +Future skills that need agent-driven invocation modes should follow this convention rather than inventing their own. + +## Working State + +The agent tracks a small set of named caches across phases. Treat these as concrete values; do not reconstruct the contract from prose at each phase boundary. + +| Cache key | Set in | Read in | Type | Default if not yet set | +|-----------|--------|---------|------|------------------------| +| `ticket_payload` | Phase 0 step 2 | All phases that need ticket data | object | n/a (must be set before Phase 1) | +| `archetype` | Phase 0 step 4 | All phases that branch on archetype | enum (Bug / Incident / Feature / Task / Spike) | n/a | +| `severity_recommendation` | Phase 2.5 step 2 (Bug/Incident) | Phase 3 display, Phase 4a body, Phase 6 due-date calc | string (severity scheme key) or `null` | `null` | +| `scope_summary_draft` | Phase 2.5 step 2 (Feature/Task/Spike) | Phase 3 display, Phase 4b body | string or `null` | `null` | +| `comment_draft` | Phase 2.5 step 4 | Phase 3 display, Phase 4a/4b/4c body | string (markdown) | `null` | +| `follow_up_needed` | Phase 2.5 step 3; flipped at Phase 3 on tag decline | Phase 4a/4b/4c branch, Phase 6 skip rule, Phase 9 transition | boolean | `false` | +| `followup_target_accountId` | Phase 2.5 step 3 | Phase 4c | string or `null` | `null` | +| `approved_post_comment` | Phase 3 main panel question 1 | Phase 4a/4b/4c entry guards | boolean | `false` | +| `approved_refine_description` | Phase 3 main panel question 2 | Phase 5 entry guard | boolean | `false` | +| `story_point_estimate` | Phase 3 main panel question 3 | Phase 6 step 3 | number or `null` | `null` | +| `approved_followup_tag` | Phase 3 main panel question 4 | Phase 3 post-panel downgrade rule | boolean | `false` | +| `comment_change_request` | "Other" channel of Phase 3 question 1 | Phase 3 revision loop | string or empty | empty | +| `refine_change_request` | "Other" channel of Phase 3 question 2 | Phase 5 invocation guidance | string or empty | empty | +| `assignment_outcome` | Phase 9 step 1 (standard path only) | Phase 10 DM placeholder `{assignment outcome}` | enum (`unassigned` / `kept assigned to you`) | `null` | + +Phase 3 reproduces the gate-relevant subset of this table inline for context, but this is the canonical glossary. + ## Connections | System | MCP server | Used for | @@ -232,9 +293,24 @@ Follow with the scenario template above. ## Workflow -For each ticket the user pastes, execute these phases in order. Pause only at the explicit confirmation gate in Phase 3. If Phase 2.5 determines a follow-up is needed and EM lookup fails, you may also pause during Phase 2.5 to ask the user who to tag. That is the only other allowed pause before Phase 3. +For each ticket the user pastes, execute these phases in order. The agent pauses at the following points and nowhere else. + +**Stops (halt the run until the user explicitly continues or overrides):** + +- **Phase 0 skip-label check:** when the ticket carries a label whose name starts with any prefix in `skip_labels` (case-insensitive), report the matched label and halt. The agent does not assign, transition, or write anything until the user explicitly says "proceed anyway". + +**Pauses (the agent is waiting on a user answer to continue):** -The workflow runs a generic core for every archetype. Phase 1 branches by archetype to call the matching investigation skill. Phases 2 (Datadog), 4 (severity assessment vs scope summary), 6 (severity + due date vs sprint placement), and 9 (assignment per `archetype_assignment_after_triage` config; Bug defaults to unassign, others to self) gate on archetype. +1. **Phase 0 first-run config branch:** when no config file exists, ask the user to pick wizard / inline / defaults. +2. **Phase 2.5 EM-lookup failure:** when a follow-up is needed and the reporter is deactivated and EM lookup fails, ask the user who to tag. +3. **Phase 3 archetype-correction pre-gate:** when issue type and content disagree, ask the user to confirm or correct the detected archetype. +4. **Phase 3 main panel:** the explicit confirmation gate (one `AskUserQuestion` with up to 4 questions side by side). +5. **Phase 3 revision loop exit (only after 3 revision rounds):** when the user keeps requesting changes via the "Other" channel after three rounds, ask Approve-as-is or Abort. +6. **Phase 5 optional second checkpoint:** only when the user explicitly opted in via the "Other" channel on Phase 3 question 2 (`refine_change_request` mentions "show me before writing" or similar). Otherwise Phase 5 is a non-interactive pause-then-write. + +No other pauses or stops are allowed. Phases 1, 2, 4a/4b/4c, 6, 7, 8, 9, and 10 run end-to-end without user prompts. + +The workflow runs a generic core for every archetype. Five phases gate on archetype: Phase 1 (investigation skill choice: `issue-investigator` for Bug/Incident, `requirements-investigator` for Feature/Task/Spike), Phase 2 (Datadog runs only for Bug/Incident; silently skipped on Feature/Task/Spike), Phase 4 (severity assessment for Bug/Incident vs scope summary for Feature/Task/Spike, with Phase 4c overriding both on the follow-up path), Phase 6 (severity + due date for Bug/Incident vs sprint placement + story points for Feature/Task/Spike), and Phase 9 (assignment per `archetype_assignment_after_triage` config; Bug defaults to unassign, others to self). --- @@ -288,10 +364,13 @@ Both skills follow the same calling convention (non-interactive, evidence-tagged 4. Summarize findings in plain prose. The structure depends on archetype: Feature gets Lead/Background/Requirements Found/Design Refs/Open Questions; Task gets Lead/Why Now/Definition of Done Found/Risks; Spike gets Lead/Question to Answer/What's Already Known/What's Unknown. **Common to both fallbacks:** Tag every finding with one of: -- `[VERIFIED]` — Directly confirmed (code read, source explicitly states this). -- `[OBSERVED]` — Pattern matches behavior, requires a logical step. -- `[INFERRED]` — Logical deduction from available info, not direct observation. -- `[UNKNOWN]` — Cannot determine from available sources. Requires runtime data. + +| Tag | Meaning | +|-----|---------| +| `[VERIFIED]` | Directly confirmed (code read, source explicitly states this). | +| `[OBSERVED]` | Pattern matches behavior, requires a logical step. | +| `[INFERRED]` | Logical deduction from available info, not direct observation. | +| `[UNKNOWN]` | Cannot determine from available sources. Requires runtime data. | Stop when you can hand the developer 2-3 concrete observations and a "Where To Look" list. The goal is orientation, not solution. @@ -323,7 +402,7 @@ Build a Logs URL for the engineer: Decide whether a reporter follow-up is warranted before presenting findings. This is the only place the follow-up decision is made. Universal across archetypes. 1. Apply the criteria in **Reporter Follow-up Policy** above. On non-bug archetypes, "fix verification" reframes as "still relevant?" (the ticket may have been overtaken by other work). -2. **For Bug or Incident: form a severity recommendation** using the Severity Criteria table at the top of this file. Match the ticket's evidence to the dimensions (User impact, Functional impact, Workaround, Data integrity, Compliance) and pick the closest level from `severity_scheme`. Cache the recommendation so Phase 3 can display it. On the standard path (`follow_up_needed = false`), Phase 4a uses the same value in the comment body and Phase 6 uses it to compute the due date. On the follow-up path, the recommendation is still cached for Phase 3 context, but Phase 4a and Phase 6 are skipped. **For Feature, Task, or Spike: skip this severity step** (severity does not apply); instead form a one-line scope summary that captures what the ticket covers and what is unclear, ready for Phase 4b to expand into a comment. Cache it for Phase 3 display. +2. **For Bug or Incident: form a severity recommendation** using the Severity Criteria table at the top of this file. Match the ticket's evidence to the dimensions (User impact, Functional impact, Workaround, Data integrity, Compliance) and pick the closest level from `severity_scheme`. Cache the recommendation as `severity_recommendation` (Working State table) so Phase 3 can display it. On the standard path (`follow_up_needed = false`), Phase 4a uses the same value in the comment body and Phase 6 uses it to compute the due date. On the follow-up path, the recommendation is still cached for Phase 3 context, but Phase 4a and Phase 6 are skipped. **For Feature, Task, or Spike: skip this severity step** (severity does not apply); instead form a one-line scope summary that captures what the ticket covers and what is unclear, ready for Phase 4b to expand into a comment. Cache it as `scope_summary_draft` for Phase 3 display. 3. **Decide the follow-up path now, before drafting the comment.** - If none of the three follow-up scenarios applies: set `follow_up_needed = false` and continue to step 4. - If one applies: set `follow_up_needed = true` and record the scenario (missing data, clarification, fix verification or relevance check). Identify who to tag using **Identifying who to tag** and cache the target `accountId` plus whether the EM preamble applies. If you need to pause to ask the user for an EM, do that now before continuing to step 4. @@ -332,7 +411,7 @@ Decide whether a reporter follow-up is warranted before presenting findings. Thi - `follow_up_needed = false`, Feature, Task, or Spike: draft the scope summary body using the Phase 4b structure (Scope Summary, What's in scope, Evidence from this ticket, Open questions). Phase 4b will post this. - `follow_up_needed = true` (any archetype): draft the question comment using the matching template from **Question comment templates** above. Keep it to one specific question. Phase 4c will post this. Phase 4a and 4b are skipped on this path, so do not draft an assessment or scope summary. - Cache the resulting markdown draft. + Cache the resulting markdown draft as `comment_draft` (Working State table). 5. **Run the `prose-style` skill on the drafted comment text from step 4.** Pass the markdown draft as input via the `Skill` tool with `name: "prose-style"`. Replace the cached draft with the returned cleaned version. Phase 3 displays the cleaned markdown draft to the user. Phase 4a, 4b, or 4c (whichever applies) converts the same cleaned text into ADF nodes at posting time. - **Defensive fallback when `prose-style` does not load:** apply these rules inline to the draft before caching: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists when the content flows naturally as sentences. Warn the user once at the start of Phase 3 that the fallback was used. @@ -355,30 +434,47 @@ Present findings to the user. Show: - The prose-style-cleaned markdown draft of the question comment from Phase 2.5, shown inline as plain markdown. Phase 4c will convert this same text to ADF on post. - What transition will happen (`waiting_reply`), who the ticket will be assigned to (the tagged person), and what will still run (refine, link, label) vs. skipped (the archetype-specific Phase 4 content, severity + due date for Bug/Incident, sprint placement for Feature/Task/Spike). -Ask the user via `AskUserQuestion`. The decisions are independent (each gates a different write), so put them in one panel as a multi-question call. `AskUserQuestion` accepts up to 4 questions per panel; pick the ones that apply to this run and batch them into a single call so the user sees all the decisions side by side instead of clicking through sequential modals. +**Working state used by this gate (excerpt of the Working State table at the top of this file).** The full canonical list lives in the Working State section above; this is a focused view of the seven in-scope cache keys (five booleans / numerics that gate writes plus two free-text channels for revision feedback): + +| Cache key | Set in | Read in | Type | +|-----------|--------|---------|------| +| `follow_up_needed` | Phase 2.5 step 3; flipped here on tag decline | Phase 4a/4b/4c branch, Phase 6 skip rule, Phase 9 transition | boolean | +| `approved_post_comment` | Phase 3 main panel question 1 | Phase 4a/4b/4c entry guards | boolean | +| `approved_refine_description` | Phase 3 main panel question 2 | Phase 5 entry guard | boolean | +| `story_point_estimate` | Phase 3 main panel question 3 | Phase 6 step 3 | number or `null` | +| `approved_followup_tag` | Phase 3 main panel question 4 | Phase 3 post-panel downgrade rule | boolean | +| `comment_change_request` | "Other" channel of question 1 | Phase 3 revision loop | string or empty | +| `refine_change_request` | "Other" channel of question 2 | Phase 5 invocation guidance | string or empty | + +Ask the user via `AskUserQuestion`. The decisions are independent (each gates a different write), so put them in one panel as a multi-question call. `AskUserQuestion` accepts up to 4 questions per panel and 2-4 options per question; pick the ones that apply to this run and batch them into a single call so the user sees all the decisions side by side instead of clicking through sequential modals. **Pre-gate (separate call, only when applicable).** Run BEFORE the main panel: -- **When the archetype detection is non-obvious** (issue type and content disagree): ask **"Detected archetype is {X}; is that right?"** as a standalone `AskUserQuestion` call. If the user picks a different archetype, redo Phase 2.5 against the correction and re-enter Phase 3. The pre-gate runs first because the archetype changes the draft content for the main panel. +- **When the archetype detection is non-obvious** (issue type and content disagree): ask **"Detected archetype is {X}; is that right?"** as a standalone `AskUserQuestion` call with the detected archetype and the next-most-likely alternative as options (the runtime adds an "Other" channel automatically for any free-text override). If the user picks a different archetype, redo Phase 2.5 against the correction and re-enter Phase 3. **Cap the correction loop at one round:** if the user corrects a second time, accept the second answer without re-asking and proceed; the agent's archetype-detection table gives at most two reasonable readings of any one ticket, and a third disagreement is the user's call to make. -**Main panel (one `AskUserQuestion` call with up to 4 questions in `questions[]`).** Always include questions 1 and 2; include 3 and 4 only when their preconditions hold. If all four apply, the panel has 4 side-by-side questions. If only the standard path applies, the panel has 2. +**Main panel (one `AskUserQuestion` call with up to 4 questions in `questions[]`).** Always include questions 1 and 2; include 3 and 4 only when their preconditions hold. The story-points question (3) and the follow-up tag question (4) are mutually exclusive at runtime: story-points fires only on `follow_up_needed = false`, tag fires only on `follow_up_needed = true`, so the panel never carries both. The maximum question count is therefore 3 (comment + refine + one of story-points or tag); the schema's 4-question cap leaves one slot of headroom for future expansion. -1. **Post the proposed Phase 4 comment?** Options: `Yes, post it`, `No, skip the comment`. The "Other" channel lets the user say "post it after these edits: ...". Cache the answer as `approved_post_comment` (boolean) and any free-text feedback as `comment_change_request`. -2. **Refine the title and description?** Options: `Yes, refine and write`, `No, leave as-is`. The "Other" channel lets the user say "refine but skip the title" or similar. Cache the answer as `approved_refine_description` (boolean) and any free-text as `refine_change_request`. The user is approving the refinement up front; Phase 5 will render the cleaned output inline before writing but does not ask again. If the user wants a second checkpoint they say so via "Other" here ("yes refine but show me before writing"). -3. **(Conditional)** When `story_points_field_name` is configured AND the archetype is Feature, Task, or Spike AND `follow_up_needed = false`: **"Story-point estimate?"** Options: `1`, `2`, `3`, `5`, `8`, `13`, `Skip` (a Fibonacci-ish set covers most teams; "Other" accepts any number). Cache the numeric answer as `story_point_estimate` or `null` on Skip. Phase 6 reads this cache; if no estimate was captured, Phase 6 silently skips the story-point write. +1. **Post the proposed Phase 4 comment?** Options: `Yes, post it`, `No, skip the comment` (2 options; the runtime adds an "Other" channel automatically for free-text). The "Other" channel lets the user say "post it after these edits: ...". Cache the boolean answer as `approved_post_comment`; cache any free-text feedback as `comment_change_request` (empty string if the user picked Yes/No without elaborating). +2. **Refine the title and description?** Options: `Yes, refine and write`, `No, leave as-is`. The "Other" channel lets the user say "refine but skip the title" or similar. Cache the boolean answer as `approved_refine_description`; cache any free-text as `refine_change_request`. The user is approving the refinement up front; Phase 5 will render the cleaned output inline before writing but does not ask again. If the user wants a second checkpoint they say so via "Other" here ("yes refine but show me before writing"); the agent then keeps Phase 5's render but adds an explicit confirmation prompt back for this run. +3. **(Conditional)** When `story_points_field_name` is configured AND the archetype is Feature, Task, or Spike AND `follow_up_needed = false`: **"Story-point estimate?"** Options (cap at 4 to fit `AskUserQuestion`'s per-question option limit): `1`, `3`, `5`, `Skip`. The "Other" channel accepts any other number (e.g., `2`, `8`, `13`, `21`). Cache the answer as `story_point_estimate`: numeric value when the user picked or typed a number; `null` when the user picked `Skip` or returned an empty/non-numeric "Other" answer. **`null` semantics: "no estimate captured". Phase 6 step 3 silently skips the story-point write. It does not mean "estimated zero".** 4. **(Conditional)** When a follow-up is proposed: **"Approve tagging {reporter or EM name} with this question?"** Options: `Yes, tag {name}`, `No, switch to standard path`. Cache as `approved_followup_tag`. -If the user feedback (the free-text "Other" channels on questions 1 and 2) requests changes, adjust the affected draft, regenerate prose-style, and re-present the same panel. Loop until the user's free-text channels are empty or the user explicitly approves the latest draft. +**Revision loop (when the user's free-text "Other" channels request changes).** If `comment_change_request` is non-empty, re-draft the comment via Phase 2.5 step 4 with the user's free-text added as guidance, then re-run prose-style on the new draft. If `refine_change_request` is non-empty, the agent does not redraft yet (Phase 5 is where the refinement actually runs), so the agent attaches the change request to the Phase 5 invocation as guidance for the refiner. The delivery mechanism follows the leading-line convention from the Skill calling-context section: the agent passes the `refine_change_request` text as part of the prompt body to the `Skill` tool (after the `Calling context: skip_preview=true.` directive line and a blank line), prefixed with `User refinement guidance:` so the refiner can recognize and apply it. After each revision pass, re-present the main panel with the updated comment draft (the refine question keeps its current cached free-text since the Phase 5 invocation has not yet run). **Cap the loop at 3 revision rounds.** A round is one complete `AskUserQuestion` panel re-presentation triggered by a non-empty "Other" channel on the previous round. After the third round, if the user still requests changes via "Other", present a final two-option `AskUserQuestion`: `Approve as-is` or `Abort this triage run`. Abort skips Phases 4-9, posts no further writes, leaves the ticket assigned and in the `investigating` transition (the side effects from Phase 0 stay), and ends with a Phase 10 DM noting the abort and quoting the user's last comment. **After the main panel returns:** -- If `approved_followup_tag = false` (the user said No to tagging): the run downgrades to the standard path. Drop the cached follow-up scenario and target `accountId`, flip `follow_up_needed = false`, re-draft the standard-path comment per Phase 2.5 step 4, run `prose-style` on it, and re-enter Phase 3 with the standard-path plan in view. Build a fresh main panel against the new draft. The user must see the standard-path Phase 4 comment, severity recommendation, due date, and any sprint or story-point proposal before any of those writes happen. +- If the tag-approval question (question 4 in this spec; see the panel-vs-spec note below for the runtime indexing rule) was answered No: the run downgrades to the standard path. Drop the cached follow-up scenario and target `accountId`, flip `follow_up_needed = false`, re-draft the standard-path comment per Phase 2.5 step 4 (assessment for Bug/Incident, scope summary for Feature/Task/Spike), run `prose-style` on it, and re-enter Phase 3 with the standard-path plan in view. Build a fresh main panel (the comment question, the refine question, and possibly the story-points question) against the new draft. The user must see the standard-path Phase 4 comment, severity recommendation, due date, and any sprint or story-point proposal before any of those writes happen. The downgrade re-run counts as a fresh Phase 3 entry; the revision-loop cap resets. + +**Panel-vs-spec numbering note.** Spec numbering treats the four questions as fixed slots: 1 = comment, 2 = refine, 3 = story-points (conditional), 4 = tag-approval (conditional). At runtime the `AskUserQuestion` panel contains only the questions whose preconditions hold, so a follow-up run sees the tag-approval question at index 3 of the panel (since story-points is mutually exclusive and absent). When the spec or downstream phases reference "the tag-approval question" or "the story-points question" by name, that semantic name resolves to whichever runtime panel index the question occupies. When matching `AskUserQuestion` answers back to flag names, identify by the question's text or its slot in the agent-built `questions[]` array, not by the spec number. - Otherwise the gate is closed and the run continues with the cached flags. -After approval, branch by `follow_up_needed` and the cached approval flags: -- `follow_up_needed = false`, archetype Bug or Incident: continue to Phase 4a if `approved_post_comment = true`; otherwise skip Phase 4a and go straight to Phase 5. -- `follow_up_needed = false`, archetype Feature, Task, or Spike: continue to Phase 4b if `approved_post_comment = true`; otherwise skip Phase 4b and go straight to Phase 5. -- `follow_up_needed = true` (any archetype): jump to Phase 4c if `approved_post_comment = true` AND question 5 (the tag-approval question) was answered Yes. If question 5 was answered No, the agent has already downgraded the run to the standard path during the gate (drop scenario + accountId, flip `follow_up_needed`, re-draft standard-path comment, re-run Phase 3 with the new plan). At this point the run continues exactly as a fresh `follow_up_needed = false` branch using the new approval flags from the re-run gate. The user has now reviewed the severity / due date / sprint / story-point / final-transition / final-assignee plan that the standard path will execute, so Phase 6 and Phase 9 may proceed. +**Branch table after the gate is closed:** + +| `follow_up_needed` | Archetype | Next phase by `approved_post_comment` | +|--------------------|-----------|---------------------------------------| +| `false` | Bug or Incident | `true` → Phase 4a → Phase 5; `false` → Phase 5 (skip 4a) | +| `false` | Feature, Task, or Spike | `true` → Phase 4b → Phase 5; `false` → Phase 5 (skip 4b) | +| `true` | any | Always `true` here (a `false` would have triggered the downgrade above and re-entered the gate as `follow_up_needed = false`). Continue to Phase 4c → Phase 5. | Phase 5 honors `approved_refine_description`: when `false`, skip the `jira-ticket-refiner` invocation, the `prose-style` styling pass, the preview, and the `editJiraIssue` write entirely; the title and description on the ticket stay untouched. When `true`, run Phase 5 as written. @@ -499,10 +595,21 @@ This phase runs two skills in sequence. First, invoke `jira-ticket-refiner` via **Fallback (when `prose-style` is not installed):** apply at minimum these rules to the refined title + description before previewing: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists when content flows naturally as sentences. Steps: -1. Build the refined title and description (`jira-ticket-refiner` invocation, or its fallback above). When invoking the skill from this phase, pass `skip_preview: true` in the calling context so the skill's own Step 7 preview-and-confirm is suppressed; the agent owns the user-facing surface for the run, and the skill should treat the agent-owned approval at Phase 3 as the gate. +1. Build the refined title and description (`jira-ticket-refiner` invocation, or its fallback above). The agent communicates the `skip_preview` instruction to the skill via the leading-line convention from the "Skill calling-context conventions" section above. The exact prompt the agent passes to the `Skill` tool is: + + ``` + Calling context: skip_preview=true. + + The orchestrator owns the user gate; do not run Step 7 preview or write via editJiraIssue. + Return the refined title and description as your final output. + + + ``` + + The first line is the machine-parseable `Calling context:` directive (a single key=value, terminated by a period). Free-text guidance to the skill goes on subsequent lines, never on the first line. The skill's body parses the first line, recognizes `skip_preview=true`, and short-circuits Step 7 (no `AskUserQuestion`, no `editJiraIssue`, no comment posting). The skill returns the refined title + description as plain text for the agent to consume. 2. Invoke the `prose-style` skill via the `Skill` tool, passing the refined title and description from step 1 as input. Replace the title and description with the cleaned versions returned by the skill (or run the inline fallback rule list above when the skill does not load). -3. Render the cleaned refined title + description to the user as inline markdown (not wrapped in an outer code fence). This is **informational, not a question** — Phase 3 already captured the user's approval to refine. The render gives the user a chance to interrupt (Ctrl+C) if something looks egregiously wrong before the write happens. Do not call `AskUserQuestion` here. Frame the output with one line above the render: "Writing the following to `{TICKET-KEY}` (interrupt within a few seconds to abort):". After rendering, proceed immediately to step 4. -4. Update via `editJiraIssue` with `contentFormat: "markdown"`. +3. Render the cleaned refined title + description to the user as inline markdown (not wrapped in an outer code fence). This is **informational, not a question**. Phase 3 already captured the user's approval to refine; the render gives the user a chance to interrupt (Ctrl+C) if something looks egregiously wrong before the write happens. Do not call `AskUserQuestion` here. Frame the output with one line above the render: ``Writing the following to `{TICKET-KEY}` in {N} seconds (interrupt to abort):``. The pause length `{N}` reads from `description_preview_pause_seconds` in the resolved config; valid values are non-negative integers. The Prerequisites validation step normalizes invalid input (negative, float, string, missing) to the default `3` and emits a one-time warning that surfaces in the Phase 10 DM, so step 3 here can trust `{N}` to be a non-negative integer. When the user explicitly opted in to a second checkpoint via the "Other" channel on Phase 3 question 2 (`refine_change_request` mentions "show me before writing" or similar), DO call `AskUserQuestion` here with options `Approve and write`, `Request changes` instead of pausing. After rendering and pausing (or after the optional confirmation), proceed to step 4. +4. Update via a single `editJiraIssue` call with `fields.summary` set to the cleaned title and `fields.description` set to the cleaned description. Use `contentFormat: "markdown"` for the description; the Atlassian MCP converts markdown to ADF on write. Never send raw ADF JSON in the description field. 5. If a "Bug Description" custom field was discoverable in prerequisites, write the same content to that field as raw ADF (`type: "doc"`, `version: 1`) in a separate `editJiraIssue` call. Some Jira instances reject markdown for that field type. If the field doesn't exist, skip this step silently. **Preserve all original media, attachments, and links.** Screenshots, videos, recordings, images, and file attachments from the original description must be carried into the refined version. Reproduce them with the same markdown image/link syntax. If the original embeds media you cannot reproduce in markdown, keep the original markup verbatim in that section. Never drop attachments, embedded images, inline links, or any referenced files. @@ -539,7 +646,7 @@ If the severity field is empty on the ticket, write the recommendation cached in 2. If `sprint_field_name` is configured in config: - Look up the active sprint for the configured `project_key` (or the inferred project key from the ticket URL) via `searchJiraIssuesUsingJql` with `sprint in openSprints() AND project = ` to find a representative sprint ID. - Set the ticket's sprint field to the active sprint ID via `editJiraIssue`. Show the user the chosen sprint name and ID before writing. -3. If `story_points_field_name` is configured AND `story_point_estimate` was cached at the Phase 3 gate (question 4) as a numeric value, write the estimate to the configured field via `editJiraIssue`. If the user answered "skip" or left the answer blank at Phase 3, leave the field unwritten. If the field name is configured but the field ID was not resolved during Prerequisites auto-discovery, skip the write silently and note the miss in the Phase 10 DM. +3. If `story_points_field_name` is configured AND `story_point_estimate` (cached at Phase 3 from the story-points question on the main panel; see the panel-vs-spec note in Phase 3 for runtime indexing) is a non-null numeric value, write the estimate to the configured field via `editJiraIssue`. If the user picked "Skip" or returned an empty/non-numeric "Other" answer at Phase 3, `story_point_estimate` is `null` and Phase 6 silently leaves the field unwritten. If the field name is configured but the field ID was not resolved during Prerequisites auto-discovery, skip the write silently and note the miss in the Phase 10 DM. 4. If neither field is configured, skip Phase 6 silently for non-bug archetypes. Skip this phase entirely when `follow_up_needed = true`. Severity, due date, sprint placement, and story points all wait until the reporter's reply comes in and the ticket is re-triaged. @@ -609,6 +716,9 @@ Pick the outcome that matches what you did: | Asked EM (reporter deactivated) | `Reporter deactivated, asked EM {name}, moved to {waiting_reply transition}` | | Comment skipped at Phase 3 | (append) `No comment posted (skipped at confirmation gate)` | | Description skipped at Phase 3 | (append) `Title and description left as-is (skipped at confirmation gate)` | +| Aborted at Phase 3 (3-revision cap reached) | `Aborted triage at confirmation gate after 3 revision rounds. Last user comment: "{quoted comment}". Ticket stays assigned to you in {investigating transition}.` | +| Config field-resolution misses (Phase 0 auto-discovery couldn't find a configured field) | (append) `Skipped {field name} write: configured field not found on this project.` | +| Invalid archetype_assignment_after_triage entry (validated at Phase 0; warning deferred to this DM) | (append) `Ignored invalid archetype_assignment_after_triage entry: {key} = {value}. Used default for that archetype.` | | Duplicate (only if user explicitly approved closure) | `Closed as duplicate of ORIGINAL-KEY` | | Severity changed | `Changed severity from {SevX} to {SevY}` | | Closed (only if user explicitly approved closure) | `Closed as {resolution}` | diff --git a/jira-issue-triage/commands/setup.md b/jira-issue-triage/commands/setup.md index 3a7b84e..3831714 100644 --- a/jira-issue-triage/commands/setup.md +++ b/jira-issue-triage/commands/setup.md @@ -128,6 +128,14 @@ Use the `Write` tool with `path: ".claude/jira-issue-triage.config.json"`. Prett ```json { + "archetype_assignment_after_triage": { + "Bug": "unassign", + "Incident": "self", + "Feature": "self", + "Task": "self", + "Spike": "self" + }, + "description_preview_pause_seconds": 3, "escalation": { "slack_channel": null, "primary_contact": null, "fallback_contact": null }, "non_bug_transitions": { "ready": null }, "project_key": null, @@ -150,13 +158,15 @@ Use the `Write` tool with `path: ".claude/jira-issue-triage.config.json"`. Prett } ``` -The four trailing optional fields (`scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`) are NOT asked in this wizard. They default to null/empty in the written config and can be added by editing the file directly. See the plugin README for documentation. +The wizard does NOT ask about these advanced fields: `scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`, `archetype_assignment_after_triage`, `description_preview_pause_seconds`. They are written with their default values shown above so that the saved JSON is a complete, browsable config. Users edit the file directly to override. See the plugin README's "Advanced configuration" section for what each field does. ### 5. Confirmation message -Print one line: +Print these lines: > Wrote `.claude/jira-issue-triage.config.json`. You can re-run `/jira-issue-triage:setup` any time to update. +> +> Advanced config keys not asked here (defaults used; edit the file directly to override): `archetype_assignment_after_triage`, `description_preview_pause_seconds`, `scope_summary_field_name`, `sprint_field_name`, `story_points_field_name`, `non_bug_transitions.ready`. See the plugin README's "Advanced configuration" section for what each one does. ## Notes diff --git a/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md b/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md index 81b3f77..228d2d2 100644 --- a/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md +++ b/jira-issue-triage/skills/jira-ticket-refiner/SKILL.md @@ -3,20 +3,26 @@ name: jira-ticket-refiner description: "Restructures a poorly written Jira ticket into a clear, self-contained document that a stranger can read cold and act on. Works on any ticket type (bug, feature, task, spike, incident). Updates the description and title via the Atlassian MCP and never deletes original content. Use when the user asks to refine, rewrite, restructure, clean up, or improve a Jira ticket." metadata: author: Taha Bikanerwala -tools: Read, mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__editJiraIssue, mcp__plugin_atlassian_atlassian__addCommentToJiraIssue +tools: AskUserQuestion, Read, mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources, mcp__plugin_atlassian_atlassian__getJiraIssue, mcp__plugin_atlassian_atlassian__editJiraIssue, mcp__plugin_atlassian_atlassian__addCommentToJiraIssue --- # Jira Ticket Refiner Take a Jira ticket that is hard to read and turn it into a document a stranger can open cold, in a year, and act on. Reorganize the content. Never delete it. Every fact in the original survives the rewrite, just placed somewhere it can be found. -This skill modifies Jira. It updates the ticket's description and title (the `fields.summary` API field) via the Atlassian MCP, and posts an optional next-steps comment when the user asks for one. +This skill modifies Jira in its default mode: it updates the ticket's description and title (writing to the `fields.description` and `fields.summary` Jira API fields, respectively) via the Atlassian MCP, and posts an optional next-steps comment when the user asks for one. **One exception:** when invoked with `Calling context: skip_preview=true.` (the `jira-issue-triage` agent does this in Phase 5), the skill operates in **read-only-return mode** and performs no Jira writes at all; it returns the refined title and description as plain text for the caller to write. See the "Calling Convention" bullets below for the full read-only-return contract. ## Calling Convention The skill works two ways. When a user pastes a ticket key and asks to refine it, run end to end. When the `jira-issue-triage` agent calls this skill in Phase 5, treat the agent's already-fetched payload and investigation findings as the source data and skip the fetch step. -- **One confirmation gate.** Always preview the rewrite before calling `editJiraIssue`. The user must approve. **Exception:** when the calling context passes `skip_preview: true` (the `jira-issue-triage` agent does this in Phase 5 because the agent already captured user approval at Phase 3 and renders its own informational preview before writing), produce the refined title and description, hand them back to the caller, and skip Step 7's preview-and-`editJiraIssue` entirely. The caller owns the write in that mode. +- **One confirmation gate.** Always preview the rewrite before calling `editJiraIssue`. The user must approve. **Exception:** when the prompt passed to the skill begins with `Calling context: skip_preview=true.` (the `jira-issue-triage` agent inserts this leading line in Phase 5 because the agent already captured user approval at Phase 3 and renders its own informational preview before writing), the skill operates in **read-only-return mode**: + - Do not call `AskUserQuestion` (no preview gate). + - Do not call `editJiraIssue` (no description or title write). + - Do not call `addCommentToJiraIssue` (no Step 8 next-steps comment, even if the input would normally trigger it). + - Do not call any other Jira-mutating MCP tool. + - The skill's only side effects are reading the cached payload and producing the refined title and description as plain-text output for the caller to consume. + - The agent (Phase 5 step 4) owns the `editJiraIssue` write; if the user wants a Step 8 next-steps comment, they request it through a separate flow after the agent run completes. - **Read-then-write.** Refuse to write before reading the description, comments, and (when relevant) changelog. - **Strict superset.** The refined ticket contains every fact from the original. Restructure, rewrite, and re-tag, never truncate. - **No solution prescription.** The skill structures information. It does not invent fixes, recommend roadmap, or editorialize on causes that are not in the ticket. From 95fd44bd2f478926c3e4d552f69f4444b1d40429 Mon Sep 17 00:00:00 2001 From: TahaBikanerwala Date: Wed, 6 May 2026 19:51:18 +0530 Subject: [PATCH 6/6] feat(azure-issue-triage): mirror plugin from marketplace Co-Authored-By: Claude Opus 4.7 (1M context) --- azure-issue-triage/.claude-plugin/plugin.json | 13 + azure-issue-triage/README.md | 348 +++++++ .../agents/azure-issue-triage.md | 861 ++++++++++++++++++ azure-issue-triage/commands/setup.md | 266 ++++++ .../skills/azure-issue-investigator/SKILL.md | 205 +++++ .../azure-requirements-investigator/SKILL.md | 152 ++++ .../references/report-template.md | 148 +++ .../skills/azure-work-item-refiner/SKILL.md | 175 ++++ .../assets/template.md | 200 ++++ .../references/azure-html-formatting.md | 90 ++ .../references/classification-guide.md | 61 ++ .../references/gathering-guide.md | 88 ++ .../references/title-guide.md | 101 ++ .../skills/prose-style/SKILL.md | 79 ++ .../prose-style/references/anti-patterns.md | 164 ++++ .../skills/prose-style/references/examples.md | 93 ++ 16 files changed, 3044 insertions(+) create mode 100644 azure-issue-triage/.claude-plugin/plugin.json create mode 100644 azure-issue-triage/README.md create mode 100644 azure-issue-triage/agents/azure-issue-triage.md create mode 100644 azure-issue-triage/commands/setup.md create mode 100644 azure-issue-triage/skills/azure-issue-investigator/SKILL.md create mode 100644 azure-issue-triage/skills/azure-requirements-investigator/SKILL.md create mode 100644 azure-issue-triage/skills/azure-requirements-investigator/references/report-template.md create mode 100644 azure-issue-triage/skills/azure-work-item-refiner/SKILL.md create mode 100644 azure-issue-triage/skills/azure-work-item-refiner/assets/template.md create mode 100644 azure-issue-triage/skills/azure-work-item-refiner/references/azure-html-formatting.md create mode 100644 azure-issue-triage/skills/azure-work-item-refiner/references/classification-guide.md create mode 100644 azure-issue-triage/skills/azure-work-item-refiner/references/gathering-guide.md create mode 100644 azure-issue-triage/skills/azure-work-item-refiner/references/title-guide.md create mode 100644 azure-issue-triage/skills/prose-style/SKILL.md create mode 100644 azure-issue-triage/skills/prose-style/references/anti-patterns.md create mode 100644 azure-issue-triage/skills/prose-style/references/examples.md diff --git a/azure-issue-triage/.claude-plugin/plugin.json b/azure-issue-triage/.claude-plugin/plugin.json new file mode 100644 index 0000000..b567947 --- /dev/null +++ b/azure-issue-triage/.claude-plugin/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "azure-issue-triage", + "version": "0.4.0", + "description": "End-to-end Azure DevOps work-item triage subagent across all archetypes (Bug, Incident, User Story, Feature, Task, Spike). Sibling of jira-issue-triage; ports the same workflow to Azure DevOps Boards: WIQL queries, Area/Iteration paths, State+Reason transitions, Microsoft.VSTS.Common.Severity. Bundles azure-issue-investigator, azure-requirements-investigator, azure-work-item-refiner, prose-style. Ships a /azure-issue-triage:setup wizard.", + "author": { + "name": "Taha Bikanerwala", + "url": "https://github.com/TahaBikanerwala" + }, + "homepage": "https://github.com/TahaBikanerwala/jt-bikanerwala-marketplace", + "repository": "https://github.com/TahaBikanerwala/jt-bikanerwala-marketplace", + "license": "MIT", + "keywords": ["azure", "azure-devops", "ado", "boards", "work-item", "wiql", "triage", "bug", "task", "subagent"] +} diff --git a/azure-issue-triage/README.md b/azure-issue-triage/README.md new file mode 100644 index 0000000..bb848d9 --- /dev/null +++ b/azure-issue-triage/README.md @@ -0,0 +1,348 @@ +# azure-issue-triage + +A Claude Code plugin that ships one subagent (`azure-issue-triage`) and a setup wizard (`/azure-issue-triage:setup`). Paste any Azure DevOps work-item URL (Bug, Incident, User Story, Feature, Task, or Spike) and tell the agent to triage. The agent assigns the work item to you, transitions it to investigating, runs the matching investigation skill, drafts an archetype-appropriate assessment comment, refines the title and description, applies a triaged tag, and posts a one-line summary on Microsoft Teams. The agent pauses at the Phase 3 confirmation gate (before posting any comment, changing the description, or updating other fields) to show you the full findings and get your approval. + +This plugin is a sibling of [`jira-issue-triage`](../jira-issue-triage/). The two plugins install side by side; the workflows are conceptually identical but call platform-specific tools. + +## What's new in v0.4.0 + +Three non-bug-flow upgrades that close the parity gap with `jira-issue-triage` and add a feature unique to the AzDO stack: + +- **Sprint placement (iteration path).** New `iteration_path_strategy` config (`null` / `"current"` / `"explicit:"`). When `"current"`, Phase 6 calls `work_list_team_iterations` for the configured `default_team` and writes the active iteration to `System.IterationPath`. When `"explicit:"`, the agent writes that path verbatim. Applies to User Story / Feature / Task / Spike on the standard path. +- **Story-point estimation prompt.** New `story_points_field` config (defaults to null). When set, the Phase 3 main panel adds a fourth question for User Story or Feature work items: `1`, `3`, `5`, or `Skip` (with "Other" accepting any other number). Phase 6 writes the estimate to the configured field; null estimates skip the write. +- **Azure Repos pull-request linking.** New `pr_linking_enabled` config (defaults to `true`). The agent regex-matches Azure Repos PR URLs in the description, comments, Teams threads, and investigator output, resolves each via `repos_get_pull_request_by_id`, proposes up to 4 at the Phase 3 panel, and writes the user-approved subset as `ArtifactLink` relations using the AzDO `vstfs:///Git/PullRequestId/...` URL form. + +All planned v0.x archetype-and-workflow surface area has now landed. Open follow-ups for v0.5.0 and later: capacity-aware sprint placement (overflow into next sprint when current is full), backfill PR links on already-merged work items, support for the `Microsoft.VSTS.CMMI.*` field family on CMMI projects. + +## What's new in v0.3.0 + +Three Bug/Incident-flow upgrades that bring the agent closer to feature parity with `jira-issue-triage`: + +- **Severity SLA due dates.** `severity_scheme` config (`due_offset_days`, `escalate_immediately`) maps each severity level to a target turnaround. Phase 6 writes `Microsoft.VSTS.Scheduling.DueDate` as `System.CreatedDate + due_offset_days`. The pre-triage value is preserved in revision history. +- **Microsoft Teams escalation routing.** A new `escalation` config block (`teams_channel`, `primary_contact`, `fallback_contact`) drives Phase 10 escalation when the recommended severity has `escalate_immediately: true`. The agent posts a separate channel message mentioning the resolved primary contact (looked up by email at session start), DMs the contact directly when no channel is configured, or no-ops when both are null. +- **EM-fallback for deactivated reporters.** When a follow-up is needed and the reporter appears unreachable, the agent now runs a three-step ladder: Teams profile manager lookup, AzDO team-admin scan, then ask-the-user with a proposed candidate. EM tagging requires explicit user approval; the question comment carries an "original reporter is unreachable" preamble. + +All deferred items shipped in v0.4.0 (see above). + +## What's new in v0.2.0 + +The archetype scope expanded from Bug + Task (v0.1.0) to all five archetypes. **Bug, Incident, User Story, Feature, Task, and Spike** all triage end-to-end now. Process-template-aware mapping in `work_item_type_map` lets Scrum (Product Backlog Item, Impediment) and CMMI (Requirement, Issue) projects override the work-item-type names. + +## Prerequisites + +### Required + +- **Azure DevOps MCP server.** The agent needs full Boards access (read work items, edit fields, post comments, query via WIQL, link work items, look up users). Microsoft ships an official server at [github.com/microsoft/azure-devops-mcp](https://github.com/microsoft/azure-devops-mcp) (`@azure-devops/mcp`). Install it through Claude Code's plugin or MCP config and authenticate against your Azure DevOps organization. + + **Tool-prefix note.** MCP tool names are scoped by however your Claude Code client mounts the server (e.g., `mcp__azure_devops__wit_get_work_item`, `mcp__plugin_ado__wit_get_work_item`, etc.). The agent body lists tool names in their commonly-used short form (`wit_get_work_item`, `wit_query_by_wiql`, `wiki_search`, `core_list_projects`). If your install prefixes them, the frontmatter and inline references in `agents/azure-issue-triage.md` need the prefix added once. The setup wizard prints the prefix it detects so you can update the agent body in one pass. + +### Recommended (the agent gracefully degrades without these) + +- **Microsoft Teams MCP server.** Used for the Phase 10 summary message. There is no canonical first-party Teams MCP yet; community options include InditexTech/mcp-teams-server and msfeldstein/MCP-MS-Teams. Without one installed, the agent prints the summary inline instead of sending a Teams message. +- **Datadog MCP server.** Used for Phase 2 log search on Bug and Incident archetypes. Without it (or for User Story / Feature / Task / Spike archetypes), Phase 2 is silently skipped. + +The plugin does not depend on Slack or Confluence. If you also use `jira-issue-triage`, both plugins coexist; their `prose-style` skills resolve via plugin namespacing. + +### Bundled skills + +The agent calls four skills during the workflow. All four ship bundled with this plugin and install automatically. + +| Skill name | Phase | Used for | Status | +|-----------|-------|----------|--------| +| `azure-issue-investigator` | Phase 1 (Bug, Incident) | Teams/AzDO/Wiki/Datadog/code investigation with evidence tags | Bundled | +| `azure-requirements-investigator` | Phase 1 (User Story, Feature, Task, Spike) | Teams/AzDO/Wiki search for prior decisions, design refs, scope; per-archetype report templates (Feature template for User Story/Feature, Task template for Task, Spike template for Spike) | Bundled | +| `azure-work-item-refiner` | Phase 5 (any archetype) | Title and description rewrite. Archetype-aware across all five archetypes. | Bundled | +| `prose-style` | Phase 2.5 + Phase 5 (any archetype) | Writing-rule application: strips em dashes, opener phrases, LLM vocabulary, bullet sprawl. Mirror of `jira-issue-triage/skills/prose-style/`. | Bundled | + +The agent body retains short defensive fallbacks for all four bundled skills. + +## Quick start + +1. Add the marketplace and install the plugin: + + ``` + /plugin marketplace add github.com/TahaBikanerwala/jt-bikanerwala-marketplace + /plugin install azure-issue-triage + ``` + +2. (Optional but recommended) Run the setup wizard: + + ``` + /azure-issue-triage:setup + ``` + + The wizard walks through six questions (organization URL, project, area path, severity field, transition mapping, Teams channel) and writes `.claude/azure-issue-triage.config.json`. You can re-run it any time to update. + + If you skip this step, the agent detects the missing config on first run and offers to walk through the same questions inline or use defaults. + +3. Verify the agent appears: open the Agent tool list and confirm `azure-issue-triage` appears. + +4. Paste any Azure DevOps work-item URL and ask the agent to triage: + + > Triage `https://dev.azure.com///_workitems/edit/12345`. + + The agent runs through phases 0-10, pauses at the Phase 3 confirmation gate, and waits for your approval before posting comments or changing fields. + +## Setup wizard + +The `/azure-issue-triage:setup` slash command walks through six questions and writes the result to `.claude/azure-issue-triage.config.json`: + +1. Organization URL (e.g., `https://dev.azure.com/contoso`). +2. Project name (or "infer from URL"). +3. Default area path prefix (optional). +4. Severity field — built-in `Microsoft.VSTS.Common.Severity` (default) or fall back to `Microsoft.VSTS.Common.Priority`. +5. State + Reason mapping for `investigating` and `waiting_reply`. +6. Teams channel for the Phase 10 summary (optional; null disables Teams). + +Auto-discovery uses `core_list_projects` and `wit_my_work_items` to suggest defaults. Failures are non-fatal; the wizard falls back to static defaults and tells you. + +The wizard never modifies Azure DevOps (read-only auto-discovery). Re-running it on an existing config offers to overwrite or keep current. + +## Configuration + +Configuration is **optional**. The agent uses sensible defaults if no config file is found. To override, run `/azure-issue-triage:setup` or create `.claude/azure-issue-triage.config.json` in your project root by hand: + +```json +{ + "organization_url": null, + "project": null, + "default_team": null, + "area_path_prefix": null, + "severity_field": "Microsoft.VSTS.Common.Severity", + "triaged_tag": "triaged", + "skip_tags": [], + "states": { + "investigating": { "state": "Active", "reason": "Investigating" }, + "waiting_reply": { "state": "Active", "reason": "Awaiting Customer" } + }, + "work_item_type_map": { + "Bug": "Bug", + "Incident": "Issue", + "User Story": "User Story", + "Feature": "Feature", + "Task": "Task", + "Spike": "Task" + }, + "archetype_assignment_after_triage": { + "Bug": "unassign", + "Incident": "self", + "User Story": "self", + "Feature": "self", + "Task": "self", + "Spike": "self" + }, + "severity_scheme": { + "1 - Critical": { "due_offset_days": 7, "escalate_immediately": true }, + "2 - High": { "due_offset_days": 14, "escalate_immediately": false }, + "3 - Medium": { "due_offset_days": 30, "escalate_immediately": false }, + "4 - Low": { "due_offset_days": 90, "escalate_immediately": false } + }, + "escalation": { + "teams_channel": null, + "primary_contact": null, + "fallback_contact": null + }, + "iteration_path_strategy": null, + "story_points_field": null, + "pr_linking_enabled": true, + "teams_channel": null, + "description_preview_pause_seconds": 3 +} +``` + +### Defaults (when config is absent) + +- `organization_url` and `project`: required at first run if not configured. The agent inspects the work-item URL and asks you to confirm or override. +- `severity_field`: `Microsoft.VSTS.Common.Severity` (the Agile process template's built-in field). Falls back to `Microsoft.VSTS.Common.Priority` if Severity is not enabled on your project. +- `triaged_tag`: `triaged` (Azure DevOps stores tags as a semicolon-delimited string; the agent appends without overwriting existing tags). +- `skip_tags`: empty (no skip rule). +- `states`: shown above. Azure DevOps requires a `State` + `Reason` pair on most transitions, so each entry is an object. Mapping depends on your process template (Agile, Scrum, CMMI). The defaults match Agile. +- `work_item_type_map`: assumes the **Agile** process template. `Bug -> Bug`, `Incident -> Issue`, `User Story -> User Story`, `Feature -> Feature`, `Task -> Task`, `Spike -> Task` (Spike has no canonical work-item type; the agent treats a Task tagged `spike` as a Spike). Override for Scrum (`User Story` becomes `Product Backlog Item`; `Incident` becomes `Impediment` or stays as a Bug with an `incident` tag) or CMMI (`User Story` becomes `Requirement`; `Incident` becomes `Issue`). Unknown work-item types pause the run and ask you which archetype to apply. +- `archetype_assignment_after_triage`: `Bug = "unassign"`; `Incident, User Story, Feature, Task, Spike = "self"`. Override per archetype. Common overrides: `"Incident": "unassign"` to route Sev-1 incidents back to the on-call pool; `"Bug": "self"` when bug triage and bug fixing are the same person. +- `severity_scheme`: 4-tier (`1 - Critical` / `2 - High` / `3 - Medium` / `4 - Low`) with 7/14/30/90-day SLA offsets. Critical is flagged for immediate escalation. The keys must exactly match the option names in your Severity field. +- `escalation`: all null. The Phase 10 summary still lands in the per-run `teams_channel` (when configured), but no separate Sev-1 channel post or contact DM happens. Set `escalation.teams_channel` and `escalation.primary_contact` to enable. +- `iteration_path_strategy`: null. Sprint placement (Phase 6 on User Story / Feature / Task / Spike) is disabled. Set to `"current"` (and configure `default_team`) or `"explicit:"` to enable. +- `story_points_field`: null. The Phase 3 story-points question and Phase 6 estimate write are disabled. Set to `"Microsoft.VSTS.Scheduling.StoryPoints"` (or your custom field's reference name) to enable. +- `pr_linking_enabled`: `true`. The agent surfaces Azure Repos PRs found during investigation as proposed `ArtifactLink` relations at the Phase 3 gate. Set to `false` to skip both the collection and the proposal. +- `teams_channel`: null. The agent prints the per-run summary inline (separate from `escalation.teams_channel`, which is for the high-severity routing). +- `description_preview_pause_seconds`: `3`. The pause between the Phase 5 informational preview and the actual write. + +### Sprint placement + +When `iteration_path_strategy = "current"`, Phase 6 calls `work_list_team_iterations` with `team: ` and `timeframe: "current"` to find the team's active sprint. The response carries the iteration path; Phase 6 writes it to `System.IterationPath`. If the team has no active sprint window (between two iterations), the agent warns once and skips the iteration write — the work item stays in its current iteration (which is usually the team's default backlog area for unscheduled work). + +When `iteration_path_strategy = "explicit:MyProject\\Backend\\Sprint 42"`, the agent writes that exact path verbatim. Useful when the work item belongs to a future sprint, a hardening sprint, or a non-default team. + +`default_team` must be set when `iteration_path_strategy = "current"` and the project has more than one team. The setup wizard prompts for it as a follow-up to Q9. + +Sprint placement only applies to User Story / Feature / Task / Spike. Bug and Incident don't use iteration paths in v0.4.0. + +### Story-point estimation + +When `story_points_field` is set and the archetype is User Story or Feature, the Phase 3 main panel adds a fourth question: `1`, `3`, `5`, or `Skip`. The "Other" channel accepts any other integer or the Fibonacci values most teams use (`2`, `8`, `13`, `21`). Phase 6 writes the chosen value to the configured field via `wit_update_work_item`. + +The estimate is voluntary; picking "Skip" leaves the field at its existing value (which may be unset). The `null` cache value means "no estimate captured" — never "estimated zero." Bug, Incident, Task, and Spike work items do not see the prompt. + +### Azure Repos pull-request linking + +The agent regex-matches Azure Repos PR URLs (`https://dev.azure.com///_git//pullrequest/`) in the description, comments, Teams threads, and investigator output. It calls `repos_get_pull_request_by_id` to resolve the title, project GUID, and repo GUID for each unique URL, capping the proposed list at 8 most-recent PRs. + +The Phase 3 main panel surfaces up to 4 of these as a multi-select question. The user picks any subset to link (or the "Other" channel to add a PR URL the agent didn't propose). Phase 7 writes each approved PR as an `ArtifactLink` relation with the AzDO-required `vstfs:///Git/PullRequestId/%2F%2F` URL form. Surplus entries (more than 4 proposed) are listed in the Phase 10 summary as "skipped (panel cap)" so the user can link them manually. + +Set `pr_linking_enabled = false` to disable both the collection step and the Phase 3 proposal entirely. + +### Severity SLA and the 4-tier default + +The default scheme aligns with the built-in `Microsoft.VSTS.Common.Severity` enum: + +| Level | Due offset | Escalate immediately | +|-------|-----------|----------------------| +| `1 - Critical` | 7 days | yes | +| `2 - High` | 14 days | no | +| `3 - Medium` | 30 days | no | +| `4 - Low` | 90 days | no | + +Phase 6 reads `severity_scheme[severity_recommendation].due_offset_days`, computes `System.CreatedDate + due_offset_days`, and writes the result to `Microsoft.VSTS.Scheduling.DueDate` (as a `YYYY-MM-DDT00:00:00Z` ISO timestamp; midnight UTC keeps the rendered date stable across viewers' time zones). When the recommended level is missing from `severity_scheme`, the agent skips the due-date write and notes the miss in the Phase 10 summary. + +Override the keys to match a renamed Severity field's options. Add or remove tiers freely; the agent uses whatever keys you define at runtime. + +### Escalation contacts + +Set `escalation.primary_contact` (and optionally `fallback_contact`) to an object with `name` and `email`: + +```json +{ + "escalation": { + "teams_channel": "Incident Response > Escalations", + "primary_contact": { "name": "Alice Kumar", "email": "alice@example.com" }, + "fallback_contact": { "name": "Bob Singh", "email": "bob@example.com" } + } +} +``` + +The agent's Prerequisites step 4 looks up Alice's Teams user descriptor via her email once per session and caches it. On any severity level marked `escalate_immediately: true`: + +- If `escalation.teams_channel` is set, the agent posts a separate Teams message to that channel mentioning Alice. +- If only `primary_contact` is set, the agent DMs Alice directly. +- If both are null, the running-user summary is the only escalation; the operator decides what to do. +- `fallback_contact` is not auto-paged on a timer; you can ask the agent ad hoc to ping the fallback later. + +Escalation only applies to Bug and Incident archetypes (severity is not used for User Story / Feature / Task / Spike). When a contact's email cannot be resolved at session start, the channel post mentions them by name only and the Phase 10 summary appends an unresolvable-contact warning. + +### EM-fallback when the reporter is deactivated + +When a follow-up question is warranted and the reporter appears unreachable (no recent assignments and Teams MCP user lookup fails), the agent runs a three-step ladder before asking you: + +1. **Teams profile manager.** If the Teams MCP exposes a profile call returning a `manager` field, the agent uses the manager's email as a candidate. +2. **AzDO team admin.** If the reporter belongs to one or more project teams, the agent proposes the team administrator (when distinct from the reporter and unique) as a candidate. +3. **Ask the user.** If the ladder produces a candidate, the agent surfaces it for confirmation. Otherwise it asks you to enter someone, or to skip the follow-up entirely. + +EM-tagged comments carry a one-sentence preamble: "The original reporter on this work item is unreachable. Tagging you as their EM (or alternate contact) to route this forward." Tagging requires explicit user approval; the agent never auto-tags an EM. + +### Process-template note + +The defaults assume the **Agile** process template. If your project uses Scrum or CMMI, override `work_item_type_map` and (for Scrum) `severity_field`: + +- **Scrum:** Replace `"User Story": "User Story"` with `"User Story": "Product Backlog Item"`. Replace `"Incident": "Issue"` with `"Incident": "Impediment"` (or `"Bug"` if your team uses Bugs tagged `incident` instead). Severity is not present by default; override `severity_field` to `Microsoft.VSTS.Common.Priority` and use the 1-4 priority field instead. +- **CMMI:** Replace `"User Story": "User Story"` with `"User Story": "Requirement"`. Bug, Task, Feature, Issue keep the same names. Severity is built in. Investigation states differ ("Proposed", "Active", "Resolved"). Override the `states` block. + +The wizard does not auto-detect the process template; it presents Agile defaults and you override the relevant fields if your project differs. + +### Skipping triage on certain work items + +Use `skip_tags` to skip triage on work items carrying any matching tag: + +```json +{ "skip_tags": ["external-vendor", "compliance-review"] } +``` + +A tag whose name *starts with* any prefix in `skip_tags` (case-insensitive) triggers the skip. The agent reports the matched tag and stops. You can override per-work-item by telling the agent to proceed anyway. + +### Custom states + +Mapping logical states to AzDO `State + Reason` pairs: + +```json +{ + "states": { + "investigating": { "state": "Active", "reason": "In Triage" }, + "waiting_reply": { "state": "Active", "reason": "Awaiting Customer Response" } + } +} +``` + +The agent reads this map and writes both `System.State` and `System.Reason` in a single `wit_update_work_item` call. + +### Datadog not installed + +Phase 2 is silently skipped. The agent never mentions Datadog in any output. No configuration needed. Phase 2 also skips silently for User Story / Feature / Task / Spike archetypes regardless of installation. + +### Teams not installed + +Phase 10 prints the summary inline as agent output instead of sending a Teams message. The agent notes once at the end of the run: "Teams DM unavailable; install a Teams MCP server to enable." + +## Workflow phases + +The workflow runs a generic core for every archetype. Four phases gate on archetype. + +| Phase | What it does | Archetypes | +|-------|--------------|------------| +| Prerequisites | Auto-discover identity, load config (with first-run wizard fallback if missing), confirm work-item-type and severity-field availability. | All | +| Phase 0 | Fetch work item via `wit_get_work_item`, run skip-tag check, detect archetype, assign to you (`System.AssignedTo`), transition to `investigating` state+reason. | All | +| Phase 1 | Investigation: `azure-issue-investigator` (Bug, Incident) or `azure-requirements-investigator` (User Story, Feature, Task, Spike). | All (skill choice gates on archetype) | +| Phase 2 | Datadog log search using signals from Phase 1. Silently suppressed on errors or when archetype is User Story / Feature / Task / Spike. | Bug, Incident | +| Phase 2.5 | Decide whether reporter follow-up is warranted. Form severity recommendation (Bug, Incident) or scope summary (User Story, Feature, Task, Spike). Draft the matching Phase 4 comment in markdown, then run `prose-style` on it. | All | +| Phase 3 | **Hard pause.** Show findings, archetype detection, and proposed updates. Asks all decisions side by side in a single `AskUserQuestion` panel. Metadata writes always run after the gate. | All | +| Phase 4a | Convert the cleaned draft to safe HTML and post the severity assessment as a discussion comment via `wit_add_work_item_comment`. | Bug, Incident | +| Phase 4b | Convert the cleaned draft to safe HTML and post the scope summary comment. The "What's in scope" body adapts to archetype (User Story / Feature: requirements found and design refs; Task: definition of done and why-now; Spike: question to answer and what's already known). | User Story, Feature, Task, Spike | +| Phase 4c | Convert the cleaned draft to safe HTML and post the follow-up question tagging the reporter. Replaces 4a or 4b. | All (only when follow_up_needed) | +| Phase 5 | Refine via `azure-work-item-refiner` (with `Calling context: skip_preview=true.` to suppress the skill's own preview gate), then run `prose-style` on the refined title and description, render the cleaned output inline as an informational preview, and write `System.Title` + `System.Description` after `description_preview_pause_seconds`. | All | +| Phase 6 | **Bug / Incident:** severity write (`Microsoft.VSTS.Common.Severity`) + due-date write (`Microsoft.VSTS.Scheduling.DueDate` computed from `severity_scheme`). **User Story / Feature / Task / Spike:** sprint placement (when `iteration_path_strategy` is set) writing `System.IterationPath`, plus optional story-point write (when `story_points_field` and `story_point_estimate` are both set). | All | +| Phase 7 | Link related/duplicate work items via `wit_update_work_item` adding `relations` entries (`System.LinkTypes.Related`, `System.LinkTypes.Hierarchy-Reverse`, `System.LinkTypes.Duplicate-Forward`). When `pr_linking_enabled = true`, also link the user-approved Azure Repos PRs as `ArtifactLink` relations using the `vstfs:///Git/PullRequestId/...` URL form. | All (PR links any archetype) | +| Phase 8 | Append the triaged tag to `System.Tags`. | All | +| Phase 9 | Final assignee per `archetype_assignment_after_triage[]`. The follow-up path moves to `waiting_reply` here; the standard path leaves the work item in `investigating` from Phase 0. | All | +| Phase 10 | Per-run summary (Teams when `teams_channel` is set; otherwise inline). Then escalation routing: when the recommended severity has `escalate_immediately: true`, post a separate channel message to `escalation.teams_channel` (or DM the resolved primary contact) mentioning `escalation.primary_contact`. | All (escalation routing on Bug/Incident only) | + +## Limitations + +The agent will never: +- Close or resolve a work item without your approval. +- Modify `Microsoft.VSTS.Common.Priority` unless `severity_field` is configured to it. +- Post a comment without showing you the text first AND getting an explicit yes at the Phase 3 gate. +- Refine the title or description without an explicit yes at the Phase 3 gate. Phase 5 then renders the cleaned output inline as an informational preview and pauses for `description_preview_pause_seconds` (default 3) before writing. +- Tag the reporter until investigation is exhausted and a specific gap blocks meaningful triage. Reporter contact is a last resort. EM-fallback (when the reporter is deactivated) is best-effort and requires explicit user approval before tagging. +- Remove or overwrite reporter-provided information during refinement (only append). +- Fabricate reproduction steps without verification. +- Mention an integration (Datadog, Teams, etc.) in any output if its API errored or returned no results. +- Drop screenshots, attachments, or inline links from the original description during refinement. + +## FAQ + +**Q: Can I run the agent on work items I'm not assigned to?** +A: Yes. Phase 0 assigns the work item to you as part of triage. After triage, the work item either stays with you or returns to the team pool based on `archetype_assignment_after_triage[]`. Defaults: Bug unassigns; Incident, User Story, Feature, Task, and Spike stay assigned. Override per archetype if your team uses a different ownership rule. + +**Q: Can I run the agent on a non-bug work item?** +A: Yes. Phase 1 calls `azure-requirements-investigator` instead of `azure-issue-investigator` for User Story, Feature, Task, and Spike. Phase 4 posts a scope summary instead of a severity assessment, with content adapted to the archetype. Phase 6 (severity write) is skipped. + +**Q: Can I run only part of the workflow?** +A: Yes. The Phase 3 confirmation gate asks separately whether to post the proposed comment and whether to refine the title and description. Answer No to either and the agent skips that write while still doing the other updates. + +**Q: Do I need to run `/azure-issue-triage:setup` before the first work item?** +A: Optional. The agent detects missing config on first run and offers to walk through the wizard inline or use defaults. + +**Q: What happens if the agent encounters an error mid-flight?** +A: It stops at the failing phase, tells you what went wrong, and asks how to proceed. It does not roll back changes already made (Azure DevOps revision history is the audit trail). + +**Q: How does archetype detection work?** +A: Phase 0 maps the work item's `System.WorkItemType` to one of Bug, Incident, User Story, Feature, Task, or Spike using the inverse of `work_item_type_map`. If the work-item type doesn't match any value in the map (e.g., a custom type), the agent pauses and asks which archetype to apply. If the type and content disagree (e.g., a Bug filed with acceptance criteria and a Figma link), the agent trusts the content and asks you to confirm at Phase 3. + +**Q: I use `jira-issue-triage` for one project and `azure-issue-triage` for another. Will they collide?** +A: No. The investigator and refiner skills are prefixed (`azure-issue-investigator`, `azure-work-item-refiner`, etc.). The `prose-style` skills in the two plugins share a name but are addressed via plugin namespacing (`jira-issue-triage:prose-style`, `azure-issue-triage:prose-style`); the agents call their own copy. + +## Contributing + +Issues and PRs welcome at the marketplace repo. The agent body is at `agents/azure-issue-triage.md`; the manifest is at `.claude-plugin/plugin.json`. Bundled skills live under `skills/`. + +## License + +MIT. See the [`LICENSE`](../../LICENSE) at the repo root. diff --git a/azure-issue-triage/agents/azure-issue-triage.md b/azure-issue-triage/agents/azure-issue-triage.md new file mode 100644 index 0000000..af734dc --- /dev/null +++ b/azure-issue-triage/agents/azure-issue-triage.md @@ -0,0 +1,861 @@ +--- +name: azure-issue-triage +description: "Triages an Azure DevOps work item end-to-end across all archetypes (Bug, Incident, User Story / Feature, Task, Spike): assigns it, transitions to investigating, runs the matching investigation skill, refines the title and description, posts an archetype-appropriate assessment comment, and posts a summary on Microsoft Teams. Use when a developer pastes an Azure DevOps work-item URL and says triage, investigate, pick up, or process." +tools: Skill, Read, Write, Bash, AskUserQuestion, wit_get_work_item, wit_update_work_item, wit_add_work_item_comment, wit_query_by_wiql, wit_get_work_item_type, wit_my_work_items, core_list_projects, core_list_project_teams, work_list_team_iterations, work_list_iterations, repos_get_pull_request_by_id, wiki_search, teams_search_messages, teams_read_thread, teams_send_message, mcp__datadog__search_datadog_logs +--- + +# Azure Issue Triage Agent + +Process an Azure DevOps work item through the full triage workflow regardless of archetype: detect whether it is a Bug, Incident, User Story / Feature, Task, or Spike; investigate using the matching skill; refine the title and description; post an archetype-appropriate assessment comment; and update the metadata fields. The workflow runs a generic core for every archetype and gates a small number of phases (severity write, investigator skill choice, comment shape) on Bug or Incident vs User Story / Feature / Task / Spike. + +All planned v0.x archetype-and-workflow surface area has landed. Open follow-ups for v0.5.0 and later: capacity-aware sprint placement (overflow into next sprint when current is full), backfill PR links on already-merged work items, support for the `Microsoft.VSTS.CMMI.*` field family on CMMI projects. + +## Tool naming note + +The frontmatter `tools` list uses short, unprefixed names. The actual MCP tool prefix depends on which Azure DevOps MCP server and which Microsoft Teams MCP server you have installed and how Claude Code mounts them. Common prefixes seen in the wild: `mcp__azure_devops__*`, `mcp__plugin_ado__*`, `mcp__plugin_azure_devops_microsoft__*`. If a tool call fails because the prefix doesn't match, edit the frontmatter once to add your prefix. + +If no Teams MCP server is installed, Phase 1 Level 1 (Teams search via the investigator skill) is silently skipped, and Phase 10's summary message prints inline as agent output instead of sending a Teams message. + +## Prerequisites + +Run these once at the start of the session and cache the results. + +### Identity and project context + +1. Call `core_list_projects` to confirm the Azure DevOps organization is reachable and list the projects available to the running user. Cache the project list keyed by name. +2. Call `wit_my_work_items` with `top: 1` to confirm work-item access and get the running user's display name and unique-name (the AzDO equivalent of an email or UPN). Cache as `assigned_to_descriptor` (the value to write back into `System.AssignedTo`). +3. If a Teams MCP is installed, search for the running user via the Teams MCP's user-lookup tool (varies by server) using the email from step 2; cache the Teams user ID. If lookup fails, treat Teams as unavailable for this run. +4. **Resolve escalation contacts.** If `escalation.primary_contact` is set in the resolved config, look up the contact via the Teams MCP using the configured email. Cache the descriptor as `escalation_target_descriptor`. Repeat for `escalation.fallback_contact` and cache as `escalation_fallback_descriptor`. If a lookup fails, leave the descriptor null and append a deferred warning for the Phase 10 summary ("Could not resolve escalation contact `{name} <{email}>`; the escalation channel will mention them by name only"). The agent never aborts the run for an escalation lookup failure; channel posts proceed without the mention element when the descriptor is null. + +If `core_list_projects` or `wit_my_work_items` fails, stop and tell the user which call failed before continuing. Never substitute hardcoded IDs. + +### Configuration + +1. Look for `.claude/azure-issue-triage.config.json` in the project root. If present, parse it and merge with the defaults below. +2. If no config file exists, pause before Phase 0 and ask the user: + + > I don't see a configuration file. Choose how to proceed: + > (a) Run `/azure-issue-triage:setup` to walk through the setup wizard, then re-paste the work-item URL. + > (b) Let me ask the same questions inline before triaging this work item. + > (c) Use defaults (sensible for most teams: Agile process template, built-in severity, no Teams DM). + +3. If the user picks (a), exit cleanly so they can run the slash command. If (b), inline-walk the wizard questions (the canonical question list lives in `commands/setup.md` inside this same plugin; mirror it exactly) and write the result via the `Write` tool with `path: ".claude/azure-issue-triage.config.json"` (pretty-print, 2-space indent, top-level keys sorted alphabetically). If (c), proceed with the defaults below and append a one-line note in the Phase 10 summary: "Triaged with default config; run /azure-issue-triage:setup any time to customize." + +The default config (used as the merge target for parsed values, and as-is when the user picks option c): + +```json +{ + "organization_url": null, + "project": null, + "default_team": null, + "area_path_prefix": null, + "severity_field": "Microsoft.VSTS.Common.Severity", + "triaged_tag": "triaged", + "skip_tags": [], + "states": { + "investigating": { "state": "Active", "reason": "Investigating" }, + "waiting_reply": { "state": "Active", "reason": "Awaiting Customer" } + }, + "work_item_type_map": { + "Bug": "Bug", + "Incident": "Issue", + "User Story": "User Story", + "Feature": "Feature", + "Task": "Task", + "Spike": "Task" + }, + "archetype_assignment_after_triage": { + "Bug": "unassign", + "Incident": "self", + "User Story": "self", + "Feature": "self", + "Task": "self", + "Spike": "self" + }, + "severity_scheme": { + "1 - Critical": { "due_offset_days": 7, "escalate_immediately": true }, + "2 - High": { "due_offset_days": 14, "escalate_immediately": false }, + "3 - Medium": { "due_offset_days": 30, "escalate_immediately": false }, + "4 - Low": { "due_offset_days": 90, "escalate_immediately": false } + }, + "escalation": { + "teams_channel": null, + "primary_contact": null, + "fallback_contact": null + }, + "iteration_path_strategy": null, + "story_points_field": null, + "pr_linking_enabled": true, + "teams_channel": null, + "description_preview_pause_seconds": 3 +} +``` + +**Validation.** The agent normalizes invalid values at session start and warns once via the Phase 10 summary rather than failing the run: + +- `description_preview_pause_seconds`: must be a non-negative integer. Negative, float, string, or null falls back to `3`. +- `archetype_assignment_after_triage`: must be an object whose values are `"unassign"` or `"self"`. A non-object value is treated as omitted and the full default applies. Per-key invalid values warn and use the archetype default. +- `states.`: must be an object with string `state` and string `reason`. Missing either field warns once and skips the corresponding transition (the work item stays in its current state). +- `work_item_type_map`: must be an object whose values are strings. The defaults assume the **Agile** process template. Process-template overrides: + - **Scrum:** `User Story` becomes `Product Backlog Item`. Set `"User Story": "Product Backlog Item"`. Bug, Task, Feature, Epic keep the same names. Scrum has no `Issue` work-item type; map `Incident` to `Impediment` or to `Bug` with an `incident` tag, depending on team convention. + - **CMMI:** `User Story` becomes `Requirement`. Set `"User Story": "Requirement"`. Bug and Task keep the same names. Map `Incident` to `Issue`. + - Unknown work-item types raise an archetype-correction prompt at Phase 3, not a hard failure. +- `severity_scheme`: must be an object whose keys are severity option labels (matching the ones returned by `wit_get_work_item_type` for the configured severity field) and whose values are objects with `due_offset_days` (non-negative integer) and `escalate_immediately` (boolean). Missing keys fall back to the default scheme; invalid values for a key warn once and use the default for that key. +- `escalation.teams_channel`: must be a string (the channel identifier in whatever shape the installed Teams MCP expects) or null. Null disables the channel post entirely; the run still summarizes inline at Phase 10. +- `escalation.primary_contact` / `escalation.fallback_contact`: must be an object `{ "name": string, "email": string }` or null. Resolved to a Teams user descriptor at session start via the Teams MCP's user-lookup tool; if the lookup fails, the contact is treated as null and a deferred warning surfaces at Phase 10. +- `iteration_path_strategy`: one of `null`, `"current"`, or `"explicit:"`. Null disables sprint placement entirely (Phase 6 skips the iteration write for User Story / Feature / Task / Spike). `"current"` reads the team's active iteration via `work_list_team_iterations` with `timeframe: "current"` and writes its `path`. `"explicit:"` writes that exact iteration path verbatim. Invalid values warn once and disable sprint placement. +- `story_points_field`: must be a string (the field's reference name, e.g., `Microsoft.VSTS.Scheduling.StoryPoints`) or null. Null disables the Phase 3 story-points question and the Phase 6 estimate write. The field must exist on the work-item type for User Story / Feature; if the field is missing, Phase 6 skips the write and warns once. +- `pr_linking_enabled`: boolean. When `true` (default), Phase 7 surfaces any Azure Repos pull-request URLs found during investigation as proposed `ArtifactLink` relations and asks the user (at the Phase 3 main panel) which to link. When `false`, the agent does not propose PR links even when it finds them; users can still link manually via the AzDO UI. + +### Severity field check (Bug and Incident) + +If the configured `severity_field` is `Microsoft.VSTS.Common.Severity` (the default), confirm it exists on the Bug work-item type for the configured project by calling `wit_get_work_item_type` with `type: "Bug"` (the Agile-default Bug + Issue types both expose it). If the field is not present (some Scrum templates omit it), warn once and fall back to `Microsoft.VSTS.Common.Priority` for severity decisions on this run. Severity is read for Bug and Incident; it is not read or written for User Story / Feature / Task / Spike. + +## Sibling Skills + +The agent invokes other skills during the workflow. Reference them by name; the `Skill` tool routes the call. When two plugins ship a skill with the same name (e.g., `prose-style` in both `jira-issue-triage` and `azure-issue-triage`), use the plugin-namespaced form `azure-issue-triage:prose-style` so the runtime resolves the call to the agent's own copy. + +**Bundled with this plugin** (always available when `azure-issue-triage` is installed): + +| Phase | Skill name | Purpose | +|-------|-----------|---------| +| Phase 1 (Bug, Incident) | `azure-issue-investigator` | Search Teams (when installed), the work item and related AzDO/Wiki pages, Datadog, then code if needed. Produces an evidence-tagged report in the 6-section bug-archetype template. | +| Phase 1 (User Story, Feature, Task, Spike) | `azure-requirements-investigator` | Search Teams (when installed) and the AzDO Wiki for prior decisions, read linked design and product docs, search related work items. Produces an evidence-tagged report in the matching archetype template (User Story / Feature use the Feature template; Task uses the Task template; Spike uses the Spike template). | +| Phase 5 (any archetype) | `azure-work-item-refiner` | Restructure the work-item description into a clear, self-contained document. Updates `System.Title` and `System.Description` via `wit_update_work_item` and never deletes original content. | +| Phase 2.5 + Phase 5 (any archetype) | `azure-issue-triage:prose-style` | Audit and rewrite drafted text so it reads like a person wrote it. Phase 2.5: clean the assessment/scope comment draft and any reporter follow-up before the Phase 3 preview. Phase 5: clean the refined title and description after `azure-work-item-refiner` runs and before the user-facing preview. Strips AI tells: em dashes, opener phrases, LLM vocabulary, bullet sprawl. | + +All four bundled skills install with the plugin. The defensive fallbacks below fire only on rare runtime load failures; they are not the expected execution path. + +### Skill calling-context conventions + +When the agent invokes a skill via the `Skill` tool, it can pass instructions to the skill by including a leading `Calling context:` line in the prompt. The convention: + +- The first line of the agent's prompt to the skill is **only** the directive: `Calling context: =[, =...].` (terminated by a period). +- The directive line carries no free-text guidance. Any human-readable instructions, payload data, or skill input go on subsequent lines after a blank line. +- The skill body parses the first line, recognizes known keys, and interprets them. Unknown keys are ignored. + +Currently defined keys: + +| Key | Value | Recognized by | Effect | +|-----|-------|---------------|--------| +| `skip_preview` | `true` / `false` | `azure-work-item-refiner` (Phase 5) | When `true`, skill skips its Step 7 preview-and-write; returns the refined title and description as plain text for the agent to write. | + +## Working State + +The agent tracks a small set of named caches across phases. Treat these as concrete values; do not reconstruct the contract from prose at each phase boundary. + +| Cache key | Set in | Read in | Type | Default if not yet set | +|-----------|--------|---------|------|------------------------| +| `work_item_payload` | Phase 0 step 2 | All phases that need work-item data | object | n/a (must be set before Phase 1) | +| `archetype` | Phase 0 step 4 | All phases that branch on archetype | enum (Bug / Incident / User Story / Feature / Task / Spike) | n/a | +| `severity_recommendation` | Phase 2.5 step 2 (Bug or Incident) | Phase 3 display, Phase 4a body, Phase 6 write | string (severity option name) or `null` | `null` | +| `scope_summary_draft` | Phase 2.5 step 2 (User Story / Feature / Task / Spike) | Phase 3 display, Phase 4b body | string or `null` | `null` | +| `comment_draft` | Phase 2.5 step 4 | Phase 3 display, Phase 4a/4b/4c body | string (markdown) | `null` | +| `follow_up_needed` | Phase 2.5 step 3; flipped at Phase 3 on tag decline | Phase 4a/4b/4c branch, Phase 6 skip rule, Phase 9 transition | boolean | `false` | +| `followup_target_descriptor` | Phase 2.5 step 3 | Phase 4c | string or `null` | `null` | +| `approved_post_comment` | Phase 3 main panel question 1 | Phase 4a/4b/4c entry guards | boolean | `false` | +| `approved_refine_description` | Phase 3 main panel question 2 | Phase 5 entry guard | boolean | `false` | +| `approved_followup_tag` | Phase 3 main panel question 4 (conditional, mutex with story-points) | Phase 3 post-panel downgrade rule | boolean | `false` | +| `comment_change_request` | "Other" channel of Phase 3 question 1 | Phase 3 revision loop | string or empty | empty | +| `refine_change_request` | "Other" channel of Phase 3 question 2 | Phase 5 invocation guidance | string or empty | empty | +| `assignment_outcome` | Phase 9 step 1 | Phase 10 summary placeholder | enum (`unassigned` / `kept assigned to you`) | `null` | +| `due_date_iso` | Phase 6 step 3 (Bug or Incident on the standard path) | Phase 10 summary placeholder | string (`YYYY-MM-DD`) or `null` | `null` | +| `escalation_target_descriptor` | Prerequisites step 4 (resolved once per session from `escalation.primary_contact.email`) | Phase 10 escalation routing | string or `null` | `null` | +| `escalation_fallback_descriptor` | Prerequisites step 4 (resolved once per session from `escalation.fallback_contact.email`) | Ad-hoc escalation only | string or `null` | `null` | +| `escalation_fired` | Phase 10 escalation routing | Phase 10 summary placeholder | boolean | `false` | +| `active_iteration_path` | Phase 6 step (resolved once per run from `work_list_team_iterations` when `iteration_path_strategy = "current"`) | Phase 6 sprint write, Phase 10 summary placeholder | string or `null` | `null` | +| `story_point_estimate` | Phase 3 main panel question 3 (conditional, mutex with tag-approval) | Phase 6 story-point write | number or `null` | `null` | +| `proposed_pr_links` | Phase 1 / Phase 2 (collected during investigation) | Phase 3 main panel display, Phase 7 link write | array of `{ url: string, title: string }` | `[]` | +| `approved_pr_links` | Phase 3 main panel (the "Other" channel of the PR-link confirmation when proposed_pr_links is non-empty) | Phase 7 link write | array of `{ url: string, title: string }` | `[]` | + +## Connections + +| System | MCP server | Used for | +|--------|-----------|----------| +| Azure DevOps Boards | The official Microsoft Azure DevOps MCP (`@azure-devops/mcp`) or compatible | Work-item fetch, edit, transition, comment, links, WIQL queries, wiki search | +| Microsoft Teams | A Teams MCP server (community-maintained; no canonical first-party choice yet) | Search messages, look up users, send the Phase 10 summary | +| Datadog | `datadog` MCP server | Log search for observability data | + +If a server is not installed or its API returns errors throughout this run, treat that integration as unavailable for this work item and proceed without it. Never mention an unavailable integration in any output. + +## Severity Criteria + +**Applies to:** Bug, Incident. Skipped for User Story / Feature / Task / Spike (severity is not used; estimation and sprint placement live in Phase 6 in later releases). + +Use these dimensions to recommend a severity. The default scheme uses the built-in `Microsoft.VSTS.Common.Severity` field's enum: `1 - Critical`, `2 - High`, `3 - Medium`, `4 - Low`. (Some templates rename these; cache the actual option labels from `wit_get_work_item_type` during Prerequisites.) + +| Dimension | What to check | +|-----------|---------------| +| User impact | All users, a segment, or a single reporter? | +| Functional impact | Core flow blocked (login, payments, scheduling) or cosmetic? | +| Workaround | Exists? Obvious to users? | +| Data integrity | Could cause data loss, corruption, or incorrect records? | +| Compliance | Affects billing, eligibility, or regulatory requirements? | + +The severity recommendation drives two Phase 6 writes: the severity field itself and an SLA-based due date (`Microsoft.VSTS.Scheduling.DueDate`). The due date is computed as `System.CreatedDate + severity_scheme[recommendation].due_offset_days`. Any level whose `escalate_immediately` flag is `true` triggers Phase 10's escalation routing. + +## Do Not Rules + +- Never close or resolve a work item unilaterally. Recommend and ask for approval. +- Never remove or overwrite reporter-provided information. Only append. +- Never drop screenshots, videos, images, recordings, file attachments, or inline links from the original description. All original media must survive into the refined version. +- Never fabricate reproduction steps you haven't verified. +- Never modify `Microsoft.VSTS.Common.Priority` unless `severity_field` is configured to it (i.e., the project has no Severity field and you fell back to Priority). +- Never comment on a work item without showing the comment text to the user and getting approval first. +- Never emit raw markdown into `System.Description` or comment bodies. Both are HTML; the agent converts the markdown draft to safe HTML using the rules in `skills/azure-work-item-refiner/references/azure-html-formatting.md` before writing. +- Never tag the reporter for clarification until investigation is exhausted and a specific gap blocks meaningful triage. Reporter contact is a last resort. +- Never tag anyone other than the reporter or, when the reporter is deactivated, the EM-fallback candidate the user explicitly approves. Never tag directors, VPs, support leads, or random team members as a shortcut. +- Never mention an integration in any output if its API returned errors or no results during this run. + +## Reporter Follow-up Policy (Last Resort) + +Reporter contact is the last thing you do before giving up on a work item, not a shortcut to skip investigation. Exhaust Phase 1 (Teams when installed, AzDO, Wiki, code) first; for Bug or Incident archetypes also exhaust Phase 2 (Datadog). Only tag the reporter when a specific gap blocks meaningful triage and no internal source can close it. + +### When asking the reporter is warranted + +Pick one of these three scenarios. If none apply, do not ask. On non-bug archetypes, "fix verification" reframes as "still relevant?" (the work item may have been overtaken by other work). + +| Scenario | Trigger | What you're asking | +|----------|---------|--------------------| +| Missing data | A field needed for triage is absent and cannot be recovered from logs, Teams, or prior work items (e.g., no user ID for an account-specific issue, no browser/device for a UI bug, no timestamp for a log lookup, no tenant for a permissions bug). | The specific missing fact. | +| Clarification | Work item contains contradictions, ambiguous symptoms, or behavior doesn't match what you found in code/logs. | A targeted yes/no or this-or-that question. | +| Fix verification (Bug or Incident) | Evidence suggests the bug is already resolved (a related PR shipped after the work item was filed, no occurrences in logs in the last N days). | Whether the issue is still reproducible. | +| Relevance check (User Story / Feature / Task / Spike) | The work item appears overtaken by other work (no activity since filed, related work shipped, scope met by another work item). | Whether the work item is still on the team's roadmap. | + +### When NOT to ask + +- You have enough evidence to hand the work item to the owning team. +- The gap can be answered with more searching you haven't tried. +- The question is about internal system behavior (the reporter won't know). +- The work item was filed within the last 24-48 hours and investigation is in flight elsewhere. + +### Identifying who to tag + +The agent tags the reporter (`System.CreatedBy`) by default. When the reporter's account is deactivated or otherwise unreachable, attempt EM-fallback resolution before asking the user. AzDO does not expose an org-chart natively, so this is a best-effort ladder; each step is allowed to fail silently. + +1. **Detect deactivation.** Call `wit_query_by_wiql` with `SELECT [System.Id] FROM WorkItems WHERE [System.AssignedTo] = '' AND [System.ChangedDate] > @today - 90` and inspect whether the reporter has been assigned anything in the last 90 days. Zero recent assignments combined with the reporter's `descriptor` failing to resolve via the Teams MCP (when installed) is the heuristic for "deactivated." (AzDO does not return an explicit active/inactive flag on `System.CreatedBy` from `wit_get_work_item`.) + +2. **Try Teams profile lookup.** If a Teams MCP is installed and exposes a `teams_get_user_profile` (or equivalent) call that returns a `manager` field, call it with the reporter's email. If the response carries a `manager.email`, propose that person as the EM. + +3. **Try AzDO team lookup.** Call `core_list_project_teams` and walk teams that the reporter is a member of (via `core_list_team_members` per team). If a team has exactly one administrator distinct from the reporter, propose that administrator as the EM. (Team admin is not always the reporter's actual EM, so this is a fallback rather than a primary signal.) + +4. **Pause and ask.** If steps 2 and 3 produce a candidate, pause and ask the user: + > The reporter on `WI #{ID}` (`{reporter name}`) appears unreachable. Best guess at their EM: `{candidate name} <{candidate email}>` (from {Teams profile / AzDO team admin}). Tag them instead? + > + > Options: `Yes, tag {candidate}`, `No, ask me to enter someone different`, `Skip the follow-up entirely`. + + If steps 2 and 3 produced no candidate: + > The reporter on `WI #{ID}` (`{reporter name}`) appears unreachable. I couldn't identify a fallback contact via Teams or team admin lookup. Who should I tag? Reply with a unique-name (UPN), email, or `skip` to drop the follow-up. + +5. **EM-tagged comment preamble.** When the user accepts a fallback candidate (Yes from step 4) or supplies someone other than the reporter, prepend one sentence to the question comment template: + + > The original reporter on this work item is unreachable. Tagging you as their EM (or alternate contact) to route this forward. + + Follow with the scenario template above. + +The EM-fallback is read-only: it queries the org for candidates and asks the user to confirm. The agent never tags an EM without explicit user approval. + +### Question comment templates + +Use the matching template. Keep each question specific. One tightly scoped question beats a list. Apply the writing rules at the bottom. + +**Missing data:** + +> @{Reporter display name} +> +> {one specific question, e.g., "What user email or ID was affected?" or "Which browser and version were you using when this happened?"} +> +> We need this to triage the work item. Reply here when you have it and we'll pick this back up. Transitioning to {waiting_reply state} in the meantime. + +**Clarification:** + +> @{Reporter display name} +> +> {specific clarifying question. Quote the part of the description that's ambiguous and offer a concrete this-or-that.} +> +> The work item points in two different directions and we want to chase the right one. Transitioning to {waiting_reply state}. + +**Fix verification (Bug or Incident):** + +> @{Reporter display name} +> +> This may already be resolved. {One-sentence evidence: e.g., "PR !1234 shipped on YYYY-MM-DD and touches the same flow" or "We're not seeing any occurrences in logs since YYYY-MM-DD."} +> +> Is the issue still happening for you? If not, we'll close this out. Transitioning to {waiting_reply state}. + +**Relevance check (User Story / Feature / Task / Spike):** + +> @{Reporter display name} +> +> This may have been overtaken by other work. {One-sentence evidence: e.g., "WI #5678 shipped on YYYY-MM-DD and covers the same scope" or "No activity here since YYYY-MM-DD; the area was reorganized."} +> +> Is this still on your team's roadmap? If not, we'll close it. Transitioning to {waiting_reply state}. + +Rules for all four templates: +- Lead with the request or the evidence. No opener phrases. +- Phase 2.5 runs the `prose-style` skill on the filled-in template before the Phase 3 preview. +- Never chain multiple questions. +- State explicitly that you're moving the work item to `waiting_reply`. +- Tag only the reporter. + +**Mention syntax in HTML.** AzDO renders user mentions as `@Display Name`. The exact HTML shape depends on the work-item host's mention parser; in practice, posting a plain-text `@Display Name` in the comment HTML produces a visible mention without a notification, and the full mention element produces both. The agent posts the full mention element when it has the unique-name; otherwise it posts plain `@Display Name` and notes in the Phase 10 summary that the mention may not have produced a notification. + +## Workflow + +For each work item the user pastes, execute these phases in order. The agent pauses at the following points and nowhere else. + +**Stops (halt the run until the user explicitly continues or overrides):** +- **Phase 0 skip-tag check:** when the work item carries a tag whose name starts with any prefix in `skip_tags` (case-insensitive), report the matched tag and halt. The agent does not assign, transition, or write anything until the user explicitly says "proceed anyway". +- **Phase 0 unmapped work-item type:** when the work-item type does not match any value in `work_item_type_map` (e.g., a custom work-item type the user's process template added), halt and ask the user which archetype to treat it as, or to skip the run. + +**Pauses (the agent is waiting on a user answer to continue):** + +1. **Phase 0 first-run config branch:** when no config file exists, ask the user to pick wizard / inline / defaults. +2. **Phase 2.5 deactivated-reporter branch:** when a follow-up is needed and the reporter appears unreachable, run the EM-fallback ladder; pause to ask the user to confirm a proposed candidate (or to enter a different person, or skip the follow-up). +3. **Phase 3 archetype-correction pre-gate:** when work-item type and content disagree, ask the user to confirm or correct the detected archetype. +4. **Phase 3 main panel:** the explicit confirmation gate (one `AskUserQuestion` with up to 3 questions side by side). +5. **Phase 3 revision loop exit (only after 3 revision rounds):** when the user keeps requesting changes via the "Other" channel after three rounds, ask Approve-as-is or Abort. +6. **Phase 5 optional second checkpoint:** only when the user explicitly opted in via the "Other" channel on Phase 3 question 2. + +The workflow gates four phases on archetype: Phase 1 (skill choice: Bug/Incident → `azure-issue-investigator`; User Story/Feature/Task/Spike → `azure-requirements-investigator`), Phase 2 (Datadog runs for Bug/Incident; silently skipped on User Story/Feature/Task/Spike), Phase 4 (severity assessment for Bug/Incident vs scope summary for User Story/Feature/Task/Spike, with Phase 4c overriding both on the follow-up path), Phase 6 (severity + due-date write for Bug/Incident vs sprint placement + optional story-point estimate for User Story/Feature/Task/Spike). + +--- + +### Phase 0: Fetch, Detect Archetype, and Assign + +1. Extract the work-item ID from the pasted URL (e.g., `12345` from `https://dev.azure.com///_workitems/edit/12345`). If `organization_url` or `project` is null in config, infer them from the URL prefix. +2. Fetch the work item via `wit_get_work_item` with `expand: "all"` and the field set: + + ``` + System.Title, System.Description, System.State, System.Reason, + System.WorkItemType, Microsoft.VSTS.Common.Priority, + Microsoft.VSTS.Common.Severity, System.Tags, System.AreaPath, + System.IterationPath, System.AssignedTo, System.CreatedBy, + System.CreatedDate, System.ChangedDate, System.Parent + ``` + + The response includes the `relations` array (links) and the work-item revisions/comments either inline (with `expand: "all"`) or via a separate fetch depending on the MCP tool's shape; if comments are not in the expanded response, fetch them via `wit_get_work_item_comments` (or the MCP's equivalent) and merge into the cached payload. Cache as `work_item_payload`. + +3. **Skip-tag check.** Parse `System.Tags` (semicolon-delimited string). Scan for any tag whose name starts with any prefix in `skip_tags` (case-insensitive). If matched, stop. Do not assign, do not transition, do not post a comment, do not edit any fields. Report this exact form and wait: + + > `WI #{ID}` already carries a skip tag (`{matched-tag}`). Skipping triage. Let me know if you want to override and proceed anyway. + + Continue past this step only on explicit user override. + +4. **Detect archetype.** Map `System.WorkItemType` to one of `Bug`, `Incident`, `User Story`, `Feature`, `Task`, `Spike` using the inverse of `work_item_type_map`. The default Agile mapping resolves: `Bug` → Bug, `Issue` → Incident, `User Story` → User Story, `Feature` → Feature, `Task` → Task. Spike has no canonical work-item type in any built-in process template; treat a Task carrying a `spike` tag as a Spike, and treat a custom `Spike` work-item type the same way. If `System.WorkItemType` does not match any value in `work_item_type_map`, pause and ask the user: "This is a `{type}` work item. Which archetype should I treat it as: Bug, Incident, User Story, Feature, Task, Spike, or skip the run?" Use their answer (and remember it for the rest of the session, but don't write it to the config file). When the work-item type matches a mapped value but the description content disagrees (e.g., type `Bug` but content is acceptance criteria and a Figma link), trust the content and surface the conflict at the Phase 3 archetype-correction pre-gate. Cache the archetype string for downstream phase gating. + +5. Assign the work item to the running user via `wit_update_work_item` with the JSON Patch: + + ```json + [ + { "op": "add", "path": "/fields/System.AssignedTo", "value": "" } + ] + ``` + + Use the cached descriptor from Prerequisites; never paste a different triager's identity. + +6. Transition to the `investigating` state by writing `System.State` and `System.Reason` from `states.investigating` in the resolved config: + + ```json + [ + { "op": "add", "path": "/fields/System.State", "value": "" }, + { "op": "add", "path": "/fields/System.Reason", "value": "" } + ] + ``` + + Combine the assignment patch (step 5) and this transition into a single `wit_update_work_item` call when the MCP tool accepts a multi-op patch document. + +--- + +### Phase 1: Investigate + +Branch by the archetype detected in Phase 0: + +- **Bug or Incident:** Invoke the `azure-issue-investigator` skill via the `Skill` tool. The skill runs the Teams (when installed) → AzDO + Wiki → Datadog → code ladder with evidence tags. Pass the cached work-item payload so the skill does not refetch. +- **User Story, Feature, Task, or Spike:** Invoke the `azure-requirements-investigator` skill via the `Skill` tool. The skill runs a Teams → AzDO + Wiki → code ladder (no Datadog level by default) and writes a per-archetype report (User Story and Feature share the Feature template; Task uses the Task template; Spike uses the Spike template). Pass the cached payload and the archetype string. + +Both skills follow the same calling convention (non-interactive, evidence-tagged output, read-only). + +**Pull-request link collection.** After the investigator skill returns, the agent post-processes the report and the cached payload to find Azure Repos PR URLs (`https://dev.azure.com///_git//pullrequest/` and the shorter `//_git//pullrequest/` form). For each unique URL, call `repos_get_pull_request_by_id` to resolve the PR title, project GUID, and repo GUID; record `{ url, title, project_id, repo_id, pr_id }` into `proposed_pr_links`. Cap the array at 8 unique PRs (most recent first). The Phase 3 main panel surfaces up to 4 of these for user approval; surplus entries are listed in the Phase 10 summary as "skipped (panel cap)". When `pr_linking_enabled = false`, this collection step is skipped. + +**Fallback for `azure-issue-investigator` (Bug or Incident path, when the skill is not installed):** + +1. If a Teams MCP is installed, search Teams with 2-3 queries via `teams_search_messages`: the work-item ID (e.g., `12345` or `AB#12345`), the most distinctive symptom or error message, the customer/area name. For relevant hits, follow up with `teams_read_thread`. +2. Search the AzDO Wiki via `wiki_search` for the feature area, system name, runbooks, known-issues pages. Search related work items via `wit_query_by_wiql` for prior items in the same area. +3. Only if steps 1 and 2 turn up nothing useful, do a light code search: use `Bash` (e.g., `grep -r 'pattern' path/`) to find error strings or endpoint names; `Read` source files near the relevant code. + +**Fallback for `azure-requirements-investigator` (User Story / Feature / Task / Spike path, when the skill is not installed):** + +1. Re-read the work item carefully (description, comments, linked work items via `relations`). +2. If a Teams MCP is installed, search Teams with 2-3 queries via `teams_search_messages`: the work-item ID, the user-story / feature / task / spike name, the area or system name. Follow relevant threads with `teams_read_thread`. +3. Search the AzDO Wiki via `wiki_search` for product briefs, design docs, ADRs, RFCs, and prior decisions in the same area. +4. Summarize findings in plain prose using the matching template (Feature: Lead / Background / Requirements Found / Design Refs / Open Questions / Where To Look; Task: Lead / Why Now / Definition of Done Found / Risks / Where To Look; Spike: Lead / Question to Answer / What's Already Known / What's Unknown / Where To Look). + +**Common to both fallbacks:** Tag every finding with `[VERIFIED]`, `[OBSERVED]`, `[INFERRED]`, or `[UNKNOWN]`. Stop when you can hand the developer 2-3 concrete observations and a "Where To Look" list. + +Warn the user once at the start of this phase if you used a fallback. + +--- + +### Phase 2: Search Datadog + +**Applies to:** Bug, Incident. +**Skipped on:** User Story, Feature, Task, Spike (silently; non-bug work items rarely have runtime telemetry to query). + +Using signals from Phase 1 (error messages, service names, entity IDs, status codes), build 1-3 targeted log queries via `search_datadog_logs`: + +- `query`: e.g., `service:my-service status:error @http.status_code:500 @user_id:abc123` +- `from`: 7 days before the work item's `System.CreatedDate`, or the timeframe mentioned in the work item +- `to`: work-item `System.CreatedDate` or now +- `limit`: 10-25 + +Build a Logs URL for the engineer: +`https://app.datadoghq.com/logs?query=&from_ts=&to_ts=` + +**Suppression rule.** If Datadog returns any error (auth, 403/404, timeout, rate limit, empty results, or any non-success), treat Datadog as unavailable for this work item. Do not mention Datadog anywhere in subsequent output. This rule overrides every later instruction that references Datadog. + +--- + +### Phase 2.5: Gap Analysis + +Decide whether a reporter follow-up is warranted before presenting findings. This is the only place the follow-up decision is made. Universal across archetypes. + +1. Apply the criteria in **Reporter Follow-up Policy** above. On non-bug archetypes, "fix verification" reframes as "still relevant?" (the work item may have been overtaken by other work). +2. **For Bug or Incident: form a severity recommendation** using the Severity Criteria table at the top of this file. Match the work item's evidence to the dimensions and pick the closest level from the cached severity options (default: `1 - Critical` / `2 - High` / `3 - Medium` / `4 - Low`). Cache the recommendation as `severity_recommendation`. **For User Story / Feature / Task / Spike: skip this severity step**; instead form a one-line scope summary that captures what the work item covers and what is unclear, ready for Phase 4b. Cache it as `scope_summary_draft`. +3. **Decide the follow-up path now, before drafting the comment.** + - If none of the four follow-up scenarios applies: set `follow_up_needed = false` and continue to step 4. + - If one applies: set `follow_up_needed = true` and record the scenario (missing data, clarification, fix verification on Bug/Incident, or relevance check on User Story/Feature/Task/Spike). Identify the reporter from `System.CreatedBy`. If the reporter's account appears unreachable, run the EM-fallback ladder (per **Identifying who to tag** above) before continuing — propose a candidate or ask the user, depending on what the ladder finds. +4. **Draft only the Phase 4 comment that will actually be posted** (still in markdown shape, not yet HTML). The branch is set by `follow_up_needed`: + - `follow_up_needed = false`, Bug or Incident: draft the assessment body using the Phase 4a structure (Assessment, Severity Recommendation, Evidence, Criteria matched). Phase 4a will post this. + - `follow_up_needed = false`, User Story / Feature / Task / Spike: draft the scope summary body using the Phase 4b structure (Scope Summary, What's in scope, Evidence, Open questions). Phase 4b will post this. The "What's in scope" body adapts to archetype (Feature: requirements found and design refs; Task: definition of done and why-now; Spike: question to answer and what's already known). + - `follow_up_needed = true` (any archetype): draft the question comment using the matching template from **Question comment templates** above. Phase 4c will post this. + + Cache the resulting markdown draft as `comment_draft`. +5. **Run the `prose-style` skill on the drafted comment text from step 4.** Pass the markdown draft as input via the `Skill` tool with `name: "azure-issue-triage:prose-style"` (the namespaced form ensures the call resolves to this plugin's copy even when `jira-issue-triage` is also installed). Replace the cached draft with the returned cleaned version. + - **Defensive fallback when `prose-style` does not load:** apply these rules inline to the draft: no em dashes, no spaced hyphens as separators, no LLM vocabulary (delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate), lead with the answer, no opener phrases, no trailing summaries on short sections, prose over bullet lists. Warn the user once at the start of Phase 3. + +--- + +### Phase 3: Confirmation Gate + +Present findings to the user. Show: + +- The detected archetype (Bug / Incident / User Story / Feature / Task / Spike) and the rule that drove the detection. +- Investigation report summary (key findings, hypotheses, evidence tags). +- Datadog findings, only if Phase 2 ran AND returned usable data. +- **Bug or Incident, `follow_up_needed = false`:** Proposed severity recommendation and computed due date (`severity_recommendation` + `due_date_iso`). The prose-style-cleaned markdown draft of the assessment comment, shown inline as plain markdown. This is the proposed Phase 4a content. +- **User Story / Feature / Task / Spike, `follow_up_needed = false`:** The prose-style-cleaned markdown draft of the scope summary comment, shown inline as plain markdown. This is the proposed Phase 4b content. When `iteration_path_strategy` is set, also display the proposed iteration path (resolved via `work_list_team_iterations` for `"current"` or read verbatim for `"explicit:..."`). +- **PR-link proposals (any archetype, when `proposed_pr_links` is non-empty):** Display the up-to-4 PRs the agent will offer for linking, with title and URL. Surplus entries (when `proposed_pr_links.length > 4`) are also shown so the user can choose to link them manually after the run. +- If `follow_up_needed = true`: the follow-up plan as a distinct block (scenario; who will be tagged and why; the prose-style-cleaned markdown draft; the transition that will happen; what will still run vs. skipped). + +Ask the user via `AskUserQuestion`. The decisions are independent (each gates a different write), so put them in one panel as a multi-question call. + +**Pre-gate (separate call, only when applicable).** Run BEFORE the main panel: + +- **When the archetype detection is non-obvious** (work-item type and content disagree): ask **"Detected archetype is {X}; is that right?"** as a standalone `AskUserQuestion` call with the detected archetype and the next-most-likely alternative as options. If the user picks a different archetype, redo Phase 2.5 against the correction and re-enter Phase 3. Cap the correction loop at one round. + +**Main panel (one `AskUserQuestion` call with up to 4 questions in `questions[]`).** Always include questions 1 and 2; include question 3 (one of story-points or tag-approval, mutually exclusive) and question 4 (PR-links) only when their preconditions hold. The schema's 4-question cap is the runtime ceiling. + +1. **Post the proposed Phase 4 comment?** Options: `Yes, post it`, `No, skip the comment`. Cache as `approved_post_comment`; cache any free-text feedback as `comment_change_request`. +2. **Refine the title and description?** Options: `Yes, refine and write`, `No, leave as-is`. Cache as `approved_refine_description`; cache any free-text as `refine_change_request`. +3. **(Conditional, one of the following — they are mutually exclusive at runtime because story-points fires only on `follow_up_needed = false` and tag-approval fires only on `follow_up_needed = true`):** + - **Story-points estimate** when `story_points_field` is configured AND archetype is `User Story` or `Feature` AND `follow_up_needed = false`: **"Story-point estimate?"** Options (cap at 4 to fit `AskUserQuestion`'s per-question option limit): `1`, `3`, `5`, `Skip`. The "Other" channel accepts any other number (e.g., `2`, `8`, `13`, `21`). Cache as `story_point_estimate`: numeric value when the user picked or typed a number; `null` when the user picked `Skip` or returned an empty/non-numeric "Other" answer. **`null` semantics: "no estimate captured". Phase 6 silently skips the story-point write. It does not mean "estimated zero".** + - **Tag-approval** when `follow_up_needed = true`: **"Approve tagging {reporter name} with this question?"** Options: `Yes, tag {name}`, `No, switch to standard path`. Cache as `approved_followup_tag`. +4. **(Conditional)** When `proposed_pr_links.length > 0` AND `pr_linking_enabled = true`: **"Link these Azure Repos pull requests to the work item?"** A multi-select question (`multiSelect: true`) listing up to 4 proposed PRs by title with each PR URL in the description. Cache the user's selected subset as `approved_pr_links`. The "Other" channel lets the user paste an additional PR URL the agent didn't propose; parse it into the same `{ url, title }` shape and append. When more than 4 PRs were detected, the surplus is dropped from the panel (cap-imposed); they are listed in the Phase 10 summary as "skipped (panel cap)" so the user can link them manually if needed. + +**Revision loop (when the user's free-text "Other" channels request changes).** If `comment_change_request` is non-empty, re-draft the comment per Phase 2.5 step 4 with the user's free-text added as guidance, then re-run prose-style. If `refine_change_request` is non-empty, attach it to the Phase 5 invocation as guidance for the refiner. After each revision pass, re-present the main panel with the updated draft. Cap the loop at 3 revision rounds. After the third round, present a final two-option `AskUserQuestion`: `Approve as-is` or `Abort this triage run`. Abort skips Phases 4-9, leaves the work item assigned and in the `investigating` state, and ends with a Phase 10 summary noting the abort. + +**After the main panel returns:** + +- If the tag-approval question was answered No: drop the cached follow-up scenario, flip `follow_up_needed = false`, re-draft the standard-path comment, run `prose-style`, and re-enter Phase 3. +- Otherwise the gate is closed and the run continues. + +Phase 5 honors `approved_refine_description`: when `false`, skip the `azure-work-item-refiner` invocation, the `prose-style` styling pass, the preview, and the `wit_update_work_item` write entirely. + +Phase 6 honors `story_point_estimate`: when null, skip the story-point write (the rest of Phase 6's writes — sprint placement on User Story / Feature / Task / Spike, severity + due date on Bug / Incident — still run). + +Phase 7 honors `approved_pr_links`: when empty, no PR-link relations are added (work-item-to-work-item links from investigation still run as before). + +The other phases (4, 7, 8, 9, 10) always run regardless of these flags; metadata writes and the final transition + Teams summary are not gated on the comment, description, story-point, or PR-link decisions. + +--- + +### Phase 4a: Severity Assessment Comment + +**Applies to:** Bug, Incident, with `approved_post_comment = true` and `follow_up_needed = false`. + +After the user approved the comment text at Phase 3, post the comment via `wit_add_work_item_comment`. Convert the prose-style-cleaned markdown draft to HTML using the rules in `skills/azure-work-item-refiner/references/azure-html-formatting.md`. Logical structure (rendered intent): + +> **Assessment:** +> +> {2-3 sentences summarizing what is broken, who is affected, how severe.} +> +> **Severity Recommendation:** {severity option name, e.g., `2 - High`} +> +> **Evidence from this work item:** +> +> - "{direct quote or paraphrase from the description, comments, or linked work items}" +> - "{another piece of evidence}" +> +> **Criteria matched:** +> +> - {which severity criteria from the table above this matches and why} + +HTML construction: each `**heading:**` line becomes `

heading:

`. Each bullet becomes `
  • ...
`. Inline work-item references (e.g., `WI #1234`) become `WI #1234`. + +Rules: +- Ground every claim in evidence from the work item, comments, or linked work items. +- Lead with what is happening, not background. +- Severity Recommendation must match an existing option of `Microsoft.VSTS.Common.Severity`. +- Never recommend a `Priority` change unless `Priority` is the configured severity field. + +--- + +### Phase 4b: Scope Summary Comment + +**Applies to:** User Story, Feature, Task, Spike, with `approved_post_comment = true` and `follow_up_needed = false`. + +After the user approved the comment text at Phase 3, post via `wit_add_work_item_comment` (HTML body). Logical structure (rendered intent): + +> **Scope Summary:** +> +> {2-3 sentences naming what this work item covers, the affected area, and the most important framing.} +> +> **What's in scope:** +> +> - **For User Story / Feature:** Requirements found, design refs, the user need being met. +> - **For Task:** Definition of done, why-now (deadline, dependency, deprecation), risks. +> - **For Spike:** Question to answer, what's already known, the time-box if known. +> +> **Evidence from this work item:** +> +> - "{direct quote or paraphrase from the description, comments, or linked work items}" +> +> **Open questions:** +> +> - {one named open question with whom it's blocked on, if anyone} + +HTML construction follows the same node patterns as Phase 4a. + +Rules: +- Ground every claim in evidence. +- Lead with what is in scope. +- Keep "Open questions" to genuine unknowns. + +--- + +### Phase 4c: Post Follow-up Question (Alternative Path) + +**Applies to:** any archetype with `follow_up_needed = true` and `approved_post_comment = true`. + +1. Confirm you have the approved (and prose-style-cleaned) draft from Phase 3 and the target reporter (or user-supplied tag target). +2. Post the follow-up via `wit_add_work_item_comment`. The comment body is HTML; if the target's unique-name is known, lead with the mention element `@Display Name`. Otherwise lead with plain `@Display Name` and note the mention may not produce a notification. +3. **Reassign the work item to the tagged person right now**, in the same turn. Call `wit_update_work_item` with the patch: + + ```json + [ + { "op": "add", "path": "/fields/System.AssignedTo", "value": "" } + ] + ``` + + Phase 9 will not touch the assignee on the follow-up path. +4. Do not post an assessment or scope summary comment. The follow-up comment is the only triage comment on the work item for this round. +5. Remember the scenario for the Phase 10 summary. + +After this phase, continue to Phase 5. + +--- + +### Phase 5: Refine the Work Item + +**Skipped when `approved_refine_description = false` from the Phase 3 gate.** + +This phase runs two skills in sequence. First, invoke `azure-work-item-refiner` via the `Skill` tool to produce the refined title and description. Then invoke `prose-style` (namespaced as `azure-issue-triage:prose-style`), passing the refiner output, to clean writing-style anti-patterns. Only after both skills run does the user-facing preview appear. + +**Fallback (when `azure-work-item-refiner` is not installed):** + +1. Use the archetype detected in Phase 0. +2. Inventory all original information + investigation findings. Include Datadog data only if Phase 2 ran and returned usable results. +3. Restructure into archetype-appropriate sections: + - **Bug or Incident:** Summary, Impact, Affected Scope, Reproduction Steps / Expected / Actual, Investigation Notes, Working Hypotheses or Root Cause. + - **User Story / Feature:** Summary, Context and Background, Requirements and Acceptance Criteria, Open Blockers. + - **Task:** Summary, Context and Background, Requirements and Acceptance Criteria (as definition of done), Solutions, Open Blockers. + - **Spike:** Summary, Context and Background, Questions to Answer, Findings (if any). +4. Rewrite the title using `{Area}: {specific problem or goal}` for any archetype, or `P{n}: {Area} {short problem statement}` for incidents, or `Spike: {Area} {question to answer}` for spikes. + +**Fallback (when `prose-style` is not installed):** apply the inline rule list from Phase 2.5 step 5 to the refined output before previewing. + +Steps: + +1. Build the refined title and description (`azure-work-item-refiner` invocation, or the fallback). The agent communicates `skip_preview` via the leading-line convention. The exact prompt: + + ``` + Calling context: skip_preview=true. + + The orchestrator owns the user gate; do not run Step 7 preview or write via wit_update_work_item. + Return the refined title and description as your final output. + + + ``` + + The skill returns the refined title + description as plain text for the agent to consume. +2. Invoke the `prose-style` skill (namespaced) with the refined title and description from step 1. Replace the title and description with the cleaned versions. +3. Convert the cleaned markdown description to HTML using `skills/azure-work-item-refiner/references/azure-html-formatting.md`. Render the cleaned title + description (markdown form) to the user as inline preview. Frame the output with one line above: + + ``` + Writing the following to WI #{ID} in {N} seconds (interrupt to abort): + ``` + + The pause length `{N}` reads from `description_preview_pause_seconds`. When the user explicitly opted in to a second checkpoint via the "Other" channel on Phase 3 question 2, call `AskUserQuestion` with options `Approve and write`, `Request changes` instead of pausing. + +4. Update via a single `wit_update_work_item` call with the JSON Patch: + + ```json + [ + { "op": "add", "path": "/fields/System.Title", "value": "" }, + { "op": "add", "path": "/fields/System.Description", "value": "" } + ] + ``` + +**Preserve all original media, attachments, and links.** Reproduce them with the same HTML markup. Never drop attachments, embedded images, inline links, or referenced files. + +Warn the user once at the start of this phase if either fallback was used. + +--- + +### Phase 6: Severity, Due Date, Sprint Placement, Story Points + +**Skipped entirely on:** `follow_up_needed = true` (everything Phase 6 writes waits until the reporter's reply comes in and the work item is re-triaged). + +The phase splits by archetype. Bug and Incident go through severity + due-date. User Story, Feature, Task, Spike go through sprint placement + story points (when configured). All writes assemble into one `wit_update_work_item` JSON Patch call when possible. + +**Bug or Incident path:** + +Read the current severity from the configured `severity_field` (default `Microsoft.VSTS.Common.Severity`) on the work-item payload. Compare against `severity_recommendation` cached in Phase 2.5. + +1. **If recommendation matches current severity:** the severity field is already correct; do not write it. Continue to step 2. +2. **If recommendation differs (or the field is empty):** the severity field needs an update; include it in the patch built in step 3. +3. **Compute the due date.** Read `severity_scheme[severity_recommendation].due_offset_days`. If the key is missing from `severity_scheme`, log a deferred warning (Phase 10) and skip the due-date write. Otherwise compute the date as `System.CreatedDate + due_offset_days` (date-only, no time component) and format as `YYYY-MM-DD`. Cache as `due_date_iso`. +4. **Write the patch.** Build a single `wit_update_work_item` JSON Patch document combining the severity change (when needed) and the due-date write: + + ```json + [ + { "op": "add", "path": "/fields/Microsoft.VSTS.Common.Severity", "value": "" }, + { "op": "add", "path": "/fields/Microsoft.VSTS.Scheduling.DueDate", "value": "T00:00:00Z" } + ] + ``` + + The DueDate field type is `DateTime` and AzDO accepts an ISO-8601 timestamp; using midnight UTC keeps the rendered date stable across viewers' time zones. Drop the severity op when step 1 already determined the field is current; drop the due-date op when step 3 logged a missing-key warning. + +5. **Do not write `Priority`** unless `severity_field` is configured to it (i.e., the project has no Severity field and you fell back to Priority). + +If the work item already has a due date set by the reporter, the Phase 6 write overwrites it. Triage owns the SLA-aligned date; reporter-supplied dates do not survive triage. The pre-triage value is preserved in the work item's revision history; nothing is destroyed. + +**User Story / Feature / Task / Spike path:** + +Severity and due date do not apply. Run the optional sprint and estimation writes: + +1. **Sprint placement.** Read `iteration_path_strategy` from the resolved config: + - **`null`:** skip sprint placement entirely. Continue to step 2. + - **`"current"`:** call `work_list_team_iterations` with `team: ` and `timeframe: "current"`. The response returns the active iteration object containing `path` (e.g., `MyProject\\Sprint 42`). Cache as `active_iteration_path`. If the call returns no current iteration (the team has no active sprint window), warn once and skip the iteration write. If `default_team` is null and the project has multiple teams, warn and skip — the agent cannot guess which team's sprint to use. + - **`"explicit:"`:** parse the path after the colon and use it as `active_iteration_path` directly. No API call needed. +2. **Story-point write.** When `story_points_field` is configured AND `story_point_estimate` (cached at Phase 3) is a non-null numeric value, include the estimate in the patch. When the field is configured but the estimate is null (the user picked Skip or returned a non-numeric "Other"), do nothing for this step — the field stays at its existing value. When `story_points_field` is configured but the field reference doesn't exist on the work-item type, warn once and skip. +3. **Write the patch.** Build a single `wit_update_work_item` JSON Patch document combining whatever ops applied: + + ```json + [ + { "op": "add", "path": "/fields/System.IterationPath", "value": "" }, + { "op": "add", "path": "/fields/Microsoft.VSTS.Scheduling.StoryPoints", "value": } + ] + ``` + + Drop either op when its precondition didn't fire. The story-point value is a JSON number, not a string. The iteration-path value is a backslash-separated string (e.g., `MyProject\\Backend\\Sprint 42`); JSON encodes the backslashes as `\\` on the wire but they decode back to single backslashes before the API write. + +If neither sprint placement nor story-point estimation applies, Phase 6 silently no-ops on this path. + +--- + +### Phase 7: Link Related Work Items and Pull Requests + +Two link types land here: work-item-to-work-item links (always) and work-item-to-pull-request links (when `pr_linking_enabled = true` and the user approved any from the Phase 3 panel). + +**Work-item links.** During investigation (Phases 1-2), collect every related work-item ID found in Teams threads, WIQL searches, the `relations` array, and comments. After the work item is refined, add links via `wit_update_work_item` with a JSON Patch operation that targets `/relations/-`: + +```json +[ + { + "op": "add", + "path": "/relations/-", + "value": { + "rel": "System.LinkTypes.Duplicate-Forward", + "url": "https://dev.azure.com//_apis/wit/workItems/", + "attributes": { "comment": "Linked during triage" } + } + } +] +``` + +| Strategy | `rel` value | +|----------|-------------| +| Duplicate (current is a duplicate of an existing canonical item) | `System.LinkTypes.Duplicate-Forward` | +| Related | `System.LinkTypes.Related` | +| Parent (current is child of an epic or feature) | `System.LinkTypes.Hierarchy-Reverse` | + +Skip any links that already exist (check the `relations` array from the Phase 0 fetch). + +**Pull-request links.** When `pr_linking_enabled = true` and `approved_pr_links` (cached at Phase 3) is non-empty, add an `ArtifactLink` relation per approved PR. Azure Repos PRs are referenced by an `vstfs://` artifact URL, not a regular HTTPS URL. Convert each approved entry's `url` field (which the agent collected as the human-facing PR URL during investigation) to the artifact form before writing. + +A PR URL like `https://dev.azure.com///_git//pullrequest/12345` maps to the artifact URL `vstfs:///Git/PullRequestId/%2F%2F12345` where `` and `` are the project and repository GUIDs (URL-encoded as `%2F` between segments). When the GUIDs are unknown (the agent has only the human URL), use the `repos_get_pull_request_by_id` tool (or equivalent) on the discovered PR ID to resolve the project and repo IDs, then build the artifact URL. + +Add via `wit_update_work_item`: + +```json +[ + { + "op": "add", + "path": "/relations/-", + "value": { + "rel": "ArtifactLink", + "url": "vstfs:///Git/PullRequestId/%2F%2F", + "attributes": { + "name": "Pull Request", + "comment": "Linked during triage" + } + } + } +] +``` + +When the project or repo ID can't be resolved (the lookup tool errors or the URL is malformed), skip that PR with a deferred warning ("Could not resolve PR ``; skipped link write"). The work-item-link writes still happen. + +The collection step lives in Phase 1 / Phase 2: while reading description, comments, Teams threads, and investigator output, the agent regex-matches Azure Repos PR URLs (`/_git//pullrequest/` or `//_git//pullrequest/` shapes) and records each unique match into `proposed_pr_links` with the title from `repos_get_pull_request_by_id`. Cap the collection at 8 (a reasonable upper bound for one work item); the Phase 3 panel sees the top 4 by recency, the rest go to "skipped (panel cap)" in the Phase 10 summary. + +--- + +### Phase 8: Tags + +Append the configured `triaged_tag` (default `triaged`) to existing tags. Read `System.Tags` (semicolon-delimited string), append `; ` if the tag is not already present, and write back via `wit_update_work_item`: + +```json +[ { "op": "add", "path": "/fields/System.Tags", "value": "existing-tag-1; existing-tag-2; triaged" } ] +``` + +Preserve existing tags exactly. Never overwrite or reorder. + +--- + +### Phase 9: Final Update + +Apply the remaining field updates and the final state. The agent does not change the work-item state in Phase 9 beyond what Phase 0 set (except on the follow-up path, which moves to `waiting_reply` here); the work item stays in `states.investigating` until the next workflow step. Phase 9's only standard-path write is the assignee per `archetype_assignment_after_triage`. + +1. **Assignee:** read the rule from `archetype_assignment_after_triage[]`. Defaults: `Bug = "unassign"`; `Incident, User Story, Feature, Task, Spike = "self"`. Apply the rule: + - **Standard path, rule = `"unassign"`:** set `System.AssignedTo` to an empty string (the AzDO equivalent of "unassigned") via `wit_update_work_item`. Cache `assignment_outcome = unassigned`. + - **Standard path, rule = `"self"`:** do not touch the assignee. Cache `assignment_outcome = kept assigned to you`. + - **Follow-up path:** Phase 4c already reassigned to the tagged person; do not touch the assignee here. + + Common overrides: + - **On-call team for incidents:** `"Incident": "unassign"`. Sev-1 incidents auto-route back to the team pool so on-call picks them up. + - **Triager owns bug fixes:** `"Bug": "self"`. Use this when bug triage and bug fixing are the same person. + +2. **State (follow-up path only):** when `follow_up_needed = true`, transition to `states.waiting_reply` (default: `state="Active", reason="Awaiting Customer"`): + + ```json + [ + { "op": "add", "path": "/fields/System.State", "value": "Active" }, + { "op": "add", "path": "/fields/System.Reason", "value": "Awaiting Customer" } + ] + ``` + + On the standard path, the work item stays in `states.investigating` from Phase 0. + +Confirm to the user what was updated. + +--- + +### Phase 10: Notification + Summary + +If a Teams MCP is installed AND `teams_channel` is configured, send the summary via `teams_send_message` to the configured channel, mentioning the running user. Format: + +> [`WI #{ID}`]({work-item URL}): {outcome} + +If Teams is not available (MCP not installed, channel not configured, or call returns an error), print the same summary inline as agent output and append: "Teams DM unavailable; install a Teams MCP server and set `teams_channel` in your config to enable." + +Pick the outcome that matches what you did: + +| Situation | Message | +|-----------|---------| +| Bug or Incident triaged, comment posted | `Triaged {Bug or Incident}, set severity {SevX}, due {due date}, posted assessment comment, {assignment outcome}` | +| Bug or Incident triaged, comment skipped at Phase 3 | `Triaged {Bug or Incident}, set severity {SevX}, due {due date}, no comment posted (skipped at confirmation gate), {assignment outcome}` | +| User Story / Feature / Task / Spike triaged, comment posted | `Triaged {archetype}, posted scope summary, {assignment outcome}` | +| User Story / Feature / Task / Spike triaged, comment skipped at Phase 3 | `Triaged {archetype}, no comment posted (skipped at confirmation gate), {assignment outcome}` | +| Sprint placement applied (User Story / Feature / Task / Spike) | (append) `Placed in sprint {active_iteration_path}` | +| Sprint placement skipped (no current iteration found, or default_team unset with multiple project teams) | (append) `Could not place in current sprint: {reason}.` | +| Story-point estimate written | (append) `Estimated {story_point_estimate} points` | +| PR links added | (append) `Linked {N} pull request{s}: {pr URLs joined by comma}` | +| PR links proposed but skipped at Phase 3 | (append) `Did not link {N} PR{s} (declined at confirmation gate)` | +| PR links surplus (more than 4 candidates) | (append) `Skipped {N} additional PR link{s} ({pr URLs}); link manually if needed.` | +| PR link write failed (couldn't resolve project or repo ID) | (append) `Could not resolve PR `{url}`; skipped link write.` | +| Asked reporter for missing data | `Asked reporter for missing info, moved to {waiting_reply state+reason}` | +| Asked reporter for clarification | `Asked reporter to clarify, moved to {waiting_reply state+reason}` | +| Asked reporter to verify fix (Bug or Incident) | `Asked reporter to confirm if still reproducing, moved to {waiting_reply state+reason}` | +| Asked reporter for relevance check (User Story / Feature / Task / Spike) | `Asked reporter if still relevant, moved to {waiting_reply state+reason}` | +| EM tagged because reporter is deactivated | (append) `Reporter is deactivated; tagged EM {name} instead.` | +| Description skipped at Phase 3 | (append) `Title and description left as-is (skipped at confirmation gate)` | +| Aborted at Phase 3 (3-revision cap reached) | `Aborted triage at confirmation gate after 3 revision rounds. Last user comment: "{quoted comment}". Work item stays assigned to you in {investigating state}.` | +| Severity changed | `Changed severity from {SevX} to {SevY}` | +| Escalation fired (Sev-1 with `escalate_immediately: true`) | (append) `Escalated in {escalation.teams_channel}, mentioned {primary contact name}.` | +| Escalation contact unresolvable | (append) `Could not resolve escalation contact {name} <{email}>; channel post mentioned them by name only.` | +| Due-date scheme miss | (append) `No due date set: severity {SevX} not in severity_scheme.` | +| Default-config first run | (append) `Triaged with default config; run /azure-issue-triage:setup any time to customize.` | +| Config validation warning (deferred from Phase 0) | (append) `Ignored invalid config: {field} = {value}. Used default.` | + +Combine multiple outcomes on one line when they apply (e.g., `Changed severity from 2 to 3. Triaged Bug, set severity 2 - High, due 2026-05-20, posted assessment comment, kept assigned to you`). + +`{assignment outcome}` resolves to the Phase 9 cache (`unassigned` or `kept assigned to you`). On the follow-up path the assignment outcome is implicit in the "Asked reporter" rows. `{due date}` resolves to `due_date_iso` (omit the clause entirely when null, e.g., on User Story / Feature / Task / Spike runs). + +### Escalation routing + +After the per-run summary lands (or as a separate channel post when no `teams_channel` is configured), apply escalation routing if all three conditions hold: + +1. The archetype is Bug or Incident. +2. `severity_scheme[severity_recommendation].escalate_immediately` is `true`. +3. `follow_up_needed` is `false` (escalation only fires on the standard path; follow-up runs wait for the reporter's reply before any escalation decision). + +When all three hold: + +- **If `escalation.teams_channel` is set:** post a separate Teams message to that channel with the format below, leading with the resolved `escalation_target_descriptor` (when non-null) so the contact gets a notification. When the descriptor is null, post the channel message with the contact's name and email in plain text and append a deferred warning ("Could not resolve escalation contact `{name} <{email}>`"). +- **If `escalation.teams_channel` is null but `escalation.primary_contact` is set:** DM the primary contact directly via the resolved `escalation_target_descriptor`. When the descriptor is null, skip the DM and append the unresolvable-contact warning. +- **If both are null:** the running-user summary is the only escalation and the operator decides what to do next. + +Channel-post format: + +> `[WI #{ID}]({work-item URL})` triaged `{SevX}`. Owner: `{primary contact name}`. Due `{due_date_iso}`. Assignment: `{assignment outcome}`. + +Cache `escalation_fired = true` so the per-run summary appends the "Escalated in ..." line. Fallback contact is not auto-paged on a timer; the operator can ask the agent to ping `escalation.fallback_contact` ad hoc later. + +--- + +## Duplicate Detection (Phase 1 helper) + +Before completing investigation, search for potential duplicates with WIQL: + +| Strategy | WIQL pattern | +|----------|--------------| +| Keywords | `SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.TeamProject] = '' AND [System.Title] CONTAINS 'keyword1' AND [System.Title] CONTAINS 'keyword2' ORDER BY [System.CreatedDate] DESC` | +| Area path | `SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.AreaPath] UNDER 'MyProject\\Backend' AND [System.State] <> 'Closed' ORDER BY [System.CreatedDate] DESC` | +| Error string | `SELECT [System.Id], [System.Title] FROM WorkItems WHERE [System.TeamProject] = '' AND ([System.Title] CONTAINS 'TypeError' OR [System.Description] CONTAINS 'Cannot read properties') ORDER BY [System.CreatedDate] DESC` | + +Link confirmed duplicates via Phase 7 with `rel: "System.LinkTypes.Duplicate-Forward"` (current is the duplicate; canonical is the link target). Use `System.LinkTypes.Related` for uncertain matches. + +--- + +## Writing Rules (always active) + +These apply to all text written to the work item, all Teams messages, and all comments. + +- Never use em dashes or spaced hyphens as separators. Restructure. +- No LLM vocabulary: delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate. +- Lead with the answer. No opener phrases. +- No trailing summaries on short sections. +- Prose over bullet lists when the content flows naturally as sentences. +- Never restate AzDO-native metadata (state, priority, work-item type, assignee, area path, iteration path, tags) in the description body. diff --git a/azure-issue-triage/commands/setup.md b/azure-issue-triage/commands/setup.md new file mode 100644 index 0000000..8eb0863 --- /dev/null +++ b/azure-issue-triage/commands/setup.md @@ -0,0 +1,266 @@ +--- +description: First-time setup wizard for azure-issue-triage. Walks through configuration questions and writes .claude/azure-issue-triage.config.json. +argument-hint: (no args) +allowed-tools: Read, Write, AskUserQuestion, core_list_projects, core_list_project_teams, wit_my_work_items, work_list_team_iterations +--- + +# azure-issue-triage Setup Wizard + +Walk the user through ten configuration questions and write the result to `.claude/azure-issue-triage.config.json`. Re-runnable: pointing the wizard at an existing config offers to overwrite or keep current. + +## Steps + +### 1. Check for existing config + +Read `.claude/azure-issue-triage.config.json` if it exists. + +- **Config exists:** Read it, show the current contents to the user as pretty-printed JSON, then ask via `AskUserQuestion`: "Overwrite the existing config?" Options: `Yes, walk through the wizard again`, `No, keep current and exit`. On "No", exit cleanly. +- **No config exists:** Continue to step 2. + +### 2. Auto-discover defaults + +Best-effort auto-discovery to suggest defaults. Failures are non-fatal; fall back to the static defaults listed in each question and tell the user the auto-discovery failed. + +1. Call `core_list_projects` to list the AzDO projects accessible to the running user. If the call returns one project, suggest it as the default `project` and `organization_url`. If multiple, list them and let the user pick at Q2. +2. Call `wit_my_work_items` with `top: 1` to confirm work-item access. The response includes the running user's display name and unique-name, useful for assignment writes; not surfaced in the wizard, but the agent uses these at runtime. +3. Pre-populate the severity field default as `Microsoft.VSTS.Common.Severity` (the Agile process template's built-in field). + +### 3. Walk through the six wizard questions + +Ask one at a time. Use `AskUserQuestion` for multiple-choice answers. Use plain free-text prompts for URLs, project names, and area paths. Confirm each answer before moving to the next question. + +#### Q1: Organization URL + +Free-text prompt: + +> Azure DevOps organization URL? Format: `https://dev.azure.com/` (no trailing slash). + +Default: pre-fill from auto-discovery if a single org was found. Validate the answer matches the URL pattern. + +**Serialization rule.** Save as a JSON string. Example: `"organization_url": "https://dev.azure.com/contoso"`. + +#### Q2: Project name + +Use `AskUserQuestion` if step 2's auto-discovery found multiple projects: + +> Which Azure DevOps project? + +Options: each accessible project name from auto-discovery, plus `Custom (type the name)`. + +If only one project was found, use it as the default and confirm via free-text prompt: + +> Default project: `{project-name}`. Press Enter to accept, or type a different project name. + +**Serialization rule.** Save as a JSON string. Example: `"project": "Contoso.Platform"`. + +#### Q3: Area path prefix (optional) + +Free-text prompt: + +> Default area path prefix for triaged work items? (Optional; press Enter to skip.) Format: `MyProject\\Backend` (use double backslashes inside JSON). + +Default: empty (the agent leaves area path untouched at triage time). + +**Serialization rule.** Empty answer maps to JSON `null`. A typed value writes the string verbatim (e.g., `"area_path_prefix": "Contoso.Platform\\\\Backend"`). + +#### Q4: Severity field + +Use `AskUserQuestion`: + +> Which field holds the severity for Bug and Incident work items? + +Options: +- `Microsoft.VSTS.Common.Severity` (recommended for Agile and CMMI process templates) +- `Microsoft.VSTS.Common.Priority` (use if your project disabled Severity, common on Scrum) +- `Custom (type the field reference name)` + +On "Custom", ask for the field reference name (e.g., `Custom.MySeverityField`) as free text. Validate the answer is non-empty. + +#### Q5: State + Reason mapping + +Two free-text prompts: + +1. > State name for "investigating"? Default: `Active`. +2. > Reason for "investigating"? Default: `Investigating`. + +Then two more: + +3. > State name for "waiting reply"? Default: `Active`. +4. > Reason for "waiting reply"? Default: `Awaiting Customer`. + +**Process-template note.** The defaults match Agile. For CMMI use `Proposed -> Active` for investigating; for Scrum `Approved -> Committed`. + +**Serialization rule.** Save under `states`: +```json +"states": { + "investigating": { "state": "Active", "reason": "Investigating" }, + "waiting_reply": { "state": "Active", "reason": "Awaiting Customer" } +} +``` + +#### Q6: Teams channel (optional) + +Free-text prompt: + +> Microsoft Teams channel for the Phase 10 per-run summary? Format: team name + channel name (e.g., `Engineering > Triage`). Press Enter to skip; the agent will print the summary inline instead of sending a Teams message. + +Default: empty. + +**Serialization rule.** Empty answer maps to JSON `null`. A typed value writes the string verbatim. The agent does not validate the channel against the live Teams instance; the actual channel ID resolution happens at runtime. + +#### Q7: Severity scheme + +Use `AskUserQuestion`: + +> Which severity scheme do you want to use? + +Options: +- `4-tier (1 - Critical, 2 - High, 3 - Medium, 4 - Low) with 7/14/30/90 day SLAs` (recommended default; matches the built-in `Microsoft.VSTS.Common.Severity` enum) +- `Custom (specify each level)` + +On "Custom", walk through each level: ask for the level name (string, must match an option of the configured `severity_field`), the `due_offset_days` integer, and via `AskUserQuestion` whether `escalate_immediately` is `Yes` or `No`. Loop until the user types `done` for the level name. The agent uses the keys you define at runtime, so make sure they exactly match the option names in your Severity custom field. + +**Serialization rule.** Save under `severity_scheme`: + +```json +"severity_scheme": { + "1 - Critical": { "due_offset_days": 7, "escalate_immediately": true }, + "2 - High": { "due_offset_days": 14, "escalate_immediately": false }, + "3 - Medium": { "due_offset_days": 30, "escalate_immediately": false }, + "4 - Low": { "due_offset_days": 90, "escalate_immediately": false } +} +``` + +#### Q8: Escalation contacts (optional) + +Three free-text sub-prompts. Each accepts an empty answer (Enter for none). + +1. > Microsoft Teams channel for high-severity escalation pings? (e.g., `Incident Response > Escalations`) Press Enter for none. +2. > Primary escalation contact? Format: `Alice Kumar `. Press Enter for none. +3. > Fallback escalation contact? Same format. Press Enter for none. + +Parse the contact strings into `{ "name": "Alice Kumar", "email": "alice@example.com" }`. If the format does not match, warn and re-prompt. + +**Serialization rule.** Empty answers map to JSON `null`, not empty strings or empty objects. Save under `escalation`: + +```json +"escalation": { + "teams_channel": null, + "primary_contact": null, + "fallback_contact": null +} +``` + +The agent's Prerequisites step 4 resolves the contacts to Teams user descriptors at session start; if a contact's email cannot be resolved, the channel post (when configured) mentions them by name only and a deferred warning surfaces in the Phase 10 summary. + +#### Q9: Sprint placement (optional) + +Use `AskUserQuestion`: + +> Should the agent place User Story / Feature / Task / Spike work items into a sprint at triage time? + +Options: +- `No, skip sprint placement` (recommended default) +- `Yes, current sprint of the default team` — uses `work_list_team_iterations` with `timeframe: "current"` at runtime to find the active iteration. Requires `default_team` to be set; the wizard prompts for it as a follow-up if it's still null. +- `Yes, a fixed iteration path I'll specify` — prompts for an explicit path like `MyProject\\Backend\\Sprint 42`. + +**Serialization rule.** "No" writes `"iteration_path_strategy": null`. "Current" writes `"iteration_path_strategy": "current"`. "Fixed" writes `"iteration_path_strategy": "explicit:"`. When "Current" is chosen and `default_team` is null, ask: + +> What's your team name in Azure DevOps? (e.g., `Platform Engineering`). The agent uses this to find the active iteration. + +Save the answer to `default_team`. + +#### Q10: Story-point estimation (optional) + +Use `AskUserQuestion`: + +> Should the agent prompt for a story-point estimate at the Phase 3 confirmation gate (User Story / Feature work items)? + +Options: +- `No, skip estimation` (recommended default) +- `Yes, write to Microsoft.VSTS.Scheduling.StoryPoints` — the built-in field on Agile and Scrum process templates. +- `Custom field reference name (type the name)` — for projects using a custom story-point field. + +**Serialization rule.** "No" writes `"story_points_field": null`. "Yes" writes `"story_points_field": "Microsoft.VSTS.Scheduling.StoryPoints"`. "Custom" writes the typed reference name verbatim. Validate non-empty. + +#### Q11: Save? + +Show the assembled config as pretty-printed JSON with sorted top-level keys. Use `AskUserQuestion`: + +> Save this config to `.claude/azure-issue-triage.config.json`? + +Options: +- `Yes, write the file` +- `No, discard and exit` +- `Edit a specific question (which one?)` + +On `Edit`, ask which question number (1-10) to revisit, re-prompt that question, and loop back to Q11. + +### 4. Write the config file + +Use the `Write` tool with `path: ".claude/azure-issue-triage.config.json"`. Pretty-print with two-space indent and sort top-level keys alphabetically for stable diffs. The full schema (with all top-level keys, in alphabetical order): + +```json +{ + "archetype_assignment_after_triage": { + "Bug": "unassign", + "Incident": "self", + "User Story": "self", + "Feature": "self", + "Task": "self", + "Spike": "self" + }, + "area_path_prefix": null, + "default_team": null, + "description_preview_pause_seconds": 3, + "escalation": { + "teams_channel": null, + "primary_contact": null, + "fallback_contact": null + }, + "iteration_path_strategy": null, + "organization_url": "https://dev.azure.com/", + "pr_linking_enabled": true, + "project": "", + "severity_field": "Microsoft.VSTS.Common.Severity", + "severity_scheme": { + "1 - Critical": { "due_offset_days": 7, "escalate_immediately": true }, + "2 - High": { "due_offset_days": 14, "escalate_immediately": false }, + "3 - Medium": { "due_offset_days": 30, "escalate_immediately": false }, + "4 - Low": { "due_offset_days": 90, "escalate_immediately": false } + }, + "skip_tags": [], + "story_points_field": null, + "states": { + "investigating": { "state": "Active", "reason": "Investigating" }, + "waiting_reply": { "state": "Active", "reason": "Awaiting Customer" } + }, + "teams_channel": null, + "triaged_tag": "triaged", + "work_item_type_map": { + "Bug": "Bug", + "Incident": "Issue", + "User Story": "User Story", + "Feature": "Feature", + "Task": "Task", + "Spike": "Task" + } +} +``` + +The wizard does NOT ask about these advanced fields: `archetype_assignment_after_triage`, `description_preview_pause_seconds`, `pr_linking_enabled`, `skip_tags`, `triaged_tag`, `work_item_type_map`. They are written with their default values shown above so that the saved JSON is a complete, browsable config. Users edit the file directly to override. (`default_team` is asked only when Q9 picks "current sprint" — it stays null otherwise.) + +### 5. Confirmation message + +Print these lines: + +> Wrote `.claude/azure-issue-triage.config.json`. You can re-run `/azure-issue-triage:setup` any time to update. +> +> Advanced config keys not asked here (defaults used; edit the file directly to override): `archetype_assignment_after_triage`, `description_preview_pause_seconds`, `default_team`, `skip_tags`, `triaged_tag`, `work_item_type_map`. See the plugin README's "Configuration" section for what each one does. + +## Notes + +- This wizard never modifies Azure DevOps. Read-only auto-discovery only. +- If `core_list_projects` or `wit_my_work_items` fails, proceed with the static defaults and tell the user the auto-discovery failed. +- The wizard does not validate the entered Teams channel name against the live Teams instance. The agent's Phase 10 attempts the send at runtime; if the channel doesn't resolve, the agent falls back to inline output and notes the failure in the summary. +- The wizard does not auto-detect the process template (Agile / Scrum / CMMI). Q5's defaults match Agile; users on Scrum or CMMI override Q5 manually based on their workflow. diff --git a/azure-issue-triage/skills/azure-issue-investigator/SKILL.md b/azure-issue-triage/skills/azure-issue-investigator/SKILL.md new file mode 100644 index 0000000..cbc2c7d --- /dev/null +++ b/azure-issue-triage/skills/azure-issue-investigator/SKILL.md @@ -0,0 +1,205 @@ +--- +name: azure-issue-investigator +description: "Investigates an Azure DevOps Bug or Incident work item by searching Microsoft Teams, the work item and related AzDO/Wiki pages, Datadog, and the codebase, then writes an evidence-tagged report in the bug-archetype template. Use when a Bug or Incident work item needs an investigation report before triage decisions are made. For User Story, Feature, Task, or Spike work items, see `azure-requirements-investigator`." +metadata: + author: Taha Bikanerwala +tools: Read, Bash, Grep, wit_get_work_item, wit_query_by_wiql, wiki_search, teams_search_messages, teams_read_thread, mcp__datadog__search_datadog_logs +--- + +# Azure Issue Investigator + +Produce a structured report that orients an engineer for an Azure DevOps Bug or Incident work item. The report names what is broken, ranks 2-3 hypotheses, lists concrete next-step queries, and tags every claim with its evidence level. + +**Scope:** Bug and Incident archetypes. For User Story, Feature, Task, or Spike work items, the `azure-issue-triage` agent calls `azure-requirements-investigator` instead. + +This skill investigates. It does not solve, post, or modify anything. + +## Calling Convention + +This skill runs without user interaction. The constraints below let it work cleanly inside the `azure-issue-triage` agent (which has its own confirmation gate) and standalone. + +- **Non-interactive.** Never ask the user a question. Inputs are inferred from the work item and search results. +- **Predictable structure.** Same six section headers every run, in the same order, with one allowed reorder for production incidents (see Adaptation Rules). +- **Same evidence tags.** Always `[VERIFIED]`, `[OBSERVED]`, `[INFERRED]`, `[UNKNOWN]`. +- **Output is the last thing.** Skill ends after the report renders. No follow-up prompts. +- **Read-only.** No `wit_update_work_item`, no `wit_add_work_item_comment`, no `teams_send_message`. Posting is the caller's job. + +## Tool naming note + +The frontmatter `tools` list uses short, unprefixed names (`wit_get_work_item`, `wit_query_by_wiql`, `wiki_search`, `teams_search_messages`). The actual tool prefix depends on which Azure DevOps MCP server and which Teams MCP server you have installed and how Claude Code mounts them (e.g., `mcp__azure_devops__wit_get_work_item`, `mcp__plugin_ado__wit_get_work_item`). If the skill's tool calls fail because the prefix doesn't match, edit the frontmatter to add your prefix once. The skill body refers to tools by their short name throughout. + +If no Teams MCP server is installed, Level 1 (Teams search) is silently skipped and the investigation starts at Level 2. + +## Search Ladder + +Investigation runs four levels top to bottom. Each level has a gate: if it produces enough evidence to write a useful report, skip the remaining levels. + +### Setup + +Before running the levels, fetch the work item once and cache it for the rest of the skill. + +1. Identify the work-item ID from the invocation context (e.g., `12345` from a pasted URL or a parameter passed by the caller). If the calling context (such as the `azure-issue-triage` agent) has already fetched the work item and exposed the payload, reuse that payload; don't fetch again. +2. If no payload is available, call `wit_get_work_item` with the ID and `expand: "all"`. Request these fields at minimum: `System.Title`, `System.Description`, `System.State`, `System.Reason`, `Microsoft.VSTS.Common.Priority`, `Microsoft.VSTS.Common.Severity`, `System.Tags`, `System.AreaPath`, `System.IterationPath`, `System.AssignedTo`, `System.CreatedBy`, `System.CreatedDate`, `System.ChangedDate`, `System.WorkItemType`, plus the `relations` array. +3. Cache the response. Reference it as "the work-item payload" throughout the skill — `System.Title`, `System.Description`, `System.CreatedDate`, `relations`, `System.CreatedBy`, etc. + +If `wit_get_work_item` fails (auth error, work item not found, network), stop and tell the caller which call failed. Do not proceed without work-item data. + +### Level 1: Teams + +Skip this level entirely if no Teams MCP server is installed (the tool calls will return tool-not-found and the level produces nothing). + +Run 2-3 queries via `teams_search_messages`: + +1. The work-item ID (e.g., `12345` or `AB#12345` if your team uses the AzDO link prefix). +2. The most distinctive symptom or error message. +3. The customer or area name combined with a key term. + +For each relevant hit, follow the thread in full with `teams_read_thread`. + +What you are looking for: +- An engineer who already identified the root cause. +- A workaround that was shared. +- A specific service, config setting, or deploy named as the culprit. +- Links to relevant pull requests, commits, or related work items. + +**Gate:** if a Teams thread contains a confirmed root cause or workaround, write the report citing that thread and skip Levels 2-4. + +### Level 2: Work Item + AzDO + Wiki + +Read the work-item payload (cached in Setup) carefully. Signals are easy to miss on a fast scan: error messages, timestamps, customer names, browser/device, the question the reporter is actually asking. + +Then search: + +- **Related work items** via `wit_query_by_wiql`. Common patterns: + - `SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.TeamProject] = '' AND [System.Description] CONTAINS '' ORDER BY [System.CreatedDate] DESC` + - `SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.TeamProject] = '' AND [System.Title] CONTAINS '' AND [System.State] <> 'Closed' ORDER BY [System.CreatedDate] DESC` + - `SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.TeamProject] = '' AND [System.AreaPath] UNDER '' ORDER BY [System.CreatedDate] DESC` +- **Linked work items.** Walk every entry in the `relations` array from the work-item payload. Read the linked work-item title, state, and the most relevant scope statement. +- **AzDO Wiki** via `wiki_search`. Look for runbooks, architecture pages, known-issues pages, onboarding docs. Use the feature area, system name, or entity type as the search term. + +For each related work item, record: ID, title, state, assignee, the most relevant finding from description or comments. +For each Wiki page, record: URL and a 1-line summary. + +**Gate:** if a runbook describes the exact scenario or a prior work item has the resolution, write the report pointing at that source. Skip Levels 3-4. + +### Level 3: Datadog + +Build queries from signals collected in Levels 1-2: error strings, service names, entity IDs, HTTP status codes. + +Call `search_datadog_logs` with: +- `query`: e.g., `service:my-service status:error @http.status_code:500 @user_id:abc123` +- `from`: 7 days before the work item's `System.CreatedDate`, or the timeframe mentioned in the work item +- `to`: work-item `System.CreatedDate` or now +- `limit`: 10-25 + +Build a Logs URL the engineer can click: +`https://app.datadoghq.com/logs?query=&from_ts=&to_ts=` + +**Suppression rule:** if Datadog returns any error (auth, 403/404, timeout, rate limit, empty results, or any non-success), treat Datadog as unavailable for this work item. Do not mention Datadog anywhere in the report. This rule overrides every other instruction that references Datadog data. + +**Gate:** if Datadog returned usable results that identify a service, an error pattern, or a timeline gap, write the report incorporating those findings. Skip Level 4 unless an external source points specifically to a code-level cause. + +### Level 4: Code + +Enter only when Levels 1-3 turned up nothing useful, OR external sources point to a code-level cause that needs tracing. + +1. **Error strings.** Use `Bash` (e.g., `grep -r 'pattern' path/`) or `Grep` to find error messages in the codebase. Identify which service owns the error. +2. **Endpoints or event handler names.** Search for route definitions or event handler names to confirm which service handles the affected flow. +3. **Observable signals.** Use `Read` to open source files near the relevant code; find logging and monitoring calls. For each call found, note the log message string and any structured tags so the "Where To Look" section can name them. +4. **Recent changes.** Run `git log --since="2 weeks ago" -- ` via `Bash` to find commits that correlate with the reported timeline. + +Stop when you can name: which service is involved, what signals are observable, and 2-3 concrete observability queries. Do not trace full call chains unless the chain itself is the finding. + +## Evidence Model + +Every claim in the report carries one of four tags. + +| Tag | Meaning | +|-----|---------| +| `[VERIFIED]` | Directly confirmed. Read in code, or a source explicitly states this. | +| `[OBSERVED]` | A pattern matches the reported behavior, but reaching the conclusion required a logical step. | +| `[INFERRED]` | Logical deduction from available information. Not directly observed. | +| `[UNKNOWN]` | Cannot determine from available sources. Requires runtime data. | + +If the finished report has more `[INFERRED]` than `[VERIFIED]` findings, the search was insufficient. Go back and search more before writing. + +Every `[UNKNOWN]` becomes a "Where To Look" item: name the runtime check that would resolve it. + +## Stop Condition + +Investigation is **done** when all three are true: + +1. There are 2-3 ranked hypotheses, most-likely first. **Exception:** if the Level 1 or Level 2 gate fired with a confirmed root cause, a single hypothesis is sufficient. +2. At least one source has been consulted at every search level the investigation reached. (If Level 1 closed via its gate, Levels 2-4 do not need sources. If Level 1 was skipped because no Teams MCP is installed, that is not a source gap; investigation just starts at Level 2.) +3. There are concrete next-step queries or files in "Where To Look". + +If any one is missing, keep investigating. + +## Report Template + +Every report has all six sections. If a section has nothing meaningful to say, write a 1-line note ("Not applicable for this work item") rather than skip the section. + +### 1. Lead + +1-2 sentences. Name what is broken and your single best hypothesis. Inline evidence tag. Do not restate the work-item title. + +Example: + +> Sessions for tenant `MapleTower` started failing at the join step yesterday after deploy `2026-04-29T18:00Z`; the new SSO middleware is the most likely cause `[OBSERVED]`. + +### 2. Scope & State + +Who is affected (one user, a segment, or all). Whether investigation is complete or needs runtime verification. Stale-work-item flag if the work item has been quiet for more than 2 weeks while the bug may already be fixed. + +### 3. Domain Context + +2-4 sentences. Define vendor names, internal acronyms, or product terminology a new team member would not know. Skip with "Not applicable" if the affected area is obvious from the title. + +### 4. What Happened + +2-4 sentences. Plain language. Include the exact error message and when the issue started if known. + +### 5. What We Found + +Narrative prose with evidence tags inline. Cover: + +- Which service or component owns the behavior. +- 2-3 hypotheses ranked by likelihood, each with its evidence trail. +- Recent changes (deploys, PRs, config) that correlate with the timeline. +- Related prior work items and what they say. + +No tables in this section. No code snippets unless the snippet itself is the finding (then keep it short). + +### 6. Where To Look + +2-5 tool-by-tool items. Each item: + +- Names the tool (code search, Teams search, admin URL, Sentry, Datadog, etc.). The list reflects tools the engineer should use after reading the report, not tools this skill itself queried. +- Gives the exact ready-to-paste query, URL, or file path. +- Says in one phrase what a hit or miss tells you. +- Datadog items appear here only if Datadog returned usable results during Level 3. The Level 3 suppression rule overrides this whenever Datadog was unavailable. + +Example: + +> - **Code search:** `grep -r 'SSO_TOKEN_EXPIRED' services/auth/` to find the error string in source. A hit identifies the service that owns the failure mode; a miss means the error originates outside the auth service. + +## Adaptation Rules + +These rules adjust section order or content emphasis. All six sections still appear every run. + +- **Found at Level 1 (Teams):** Section 5 leads with the Teams source and links the thread. Sections 3, 4 may be 1 line each. +- **Found at Level 2 (runbook or prior work item):** Section 5 leads with the source. Same brevity allowed elsewhere. +- **Required Levels 3-4 (code/logs):** Section 5 includes code references inline as `path/to/file.ext:line`. No long code snippets unless the snippet is the finding. +- **Production incident (live impact):** Reorder. Put Section 6 ("Where To Look") immediately after Section 1 ("Lead"). Sections 2-5 follow. Engineers reading this need next actions before context. +- **Vague work item (almost no signal):** Section 5 describes what was searched and what is unknown. Section 6 ends with a single `Where To Look` item naming the specific information the reporter could provide, phrased as a concrete question for the owning team to use if they choose to contact the reporter. The skill itself never contacts the reporter. + +## Writing Rules + +These apply to all text in the report. + +- No em dashes or spaced hyphens as separators. Em dashes inside parenthetical asides are fine. +- No LLM vocabulary: delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate. +- Lead with the answer. No opener phrases. +- No trailing summaries on short sections. +- Prose over bullet lists when the content flows naturally as sentences. +- Never present unverified analysis as a confirmed root cause. diff --git a/azure-issue-triage/skills/azure-requirements-investigator/SKILL.md b/azure-issue-triage/skills/azure-requirements-investigator/SKILL.md new file mode 100644 index 0000000..259099b --- /dev/null +++ b/azure-issue-triage/skills/azure-requirements-investigator/SKILL.md @@ -0,0 +1,152 @@ +--- +name: azure-requirements-investigator +description: "Investigates a non-bug Azure DevOps work item (User Story, Feature, Task, Spike) by reading the work item and linked design or product docs, searching Microsoft Teams and AzDO Wiki for prior decisions, and producing an evidence-tagged orientation report. Use when a developer is about to pick up a non-bug work item and wants context before starting work." +metadata: + author: Taha Bikanerwala +tools: Read, Bash, Grep, wit_get_work_item, wit_query_by_wiql, wiki_search, teams_search_messages, teams_read_thread +--- + +# Azure Requirements Investigator + +Produce a structured report that orients a developer about to pick up a User Story, Feature, Task, or Spike work item. The report names what is being built (or asked), surfaces prior decisions found in Microsoft Teams and AzDO Wiki, lists the open questions that block start-of-work, and tags every claim with its evidence level. + +**Scope:** User Story, Feature, Task, Spike. The agent's Phase 0 routes any of these archetypes into this skill. Bug and Incident go to `azure-issue-investigator` instead. + +This skill investigates. It does not solve, post, or modify anything. + +## Calling Convention + +This skill runs without user interaction. The constraints below let it work cleanly inside the `azure-issue-triage` agent (which has its own confirmation gate) and standalone. + +- **Non-interactive.** Never ask the user a question. Inputs are inferred from the work item and search results. +- **Predictable structure.** Per-archetype templates with fixed section orders. The archetype is passed by the caller; when running standalone, infer it from the work-item-type field. +- **Same evidence tags.** Always `[VERIFIED]`, `[OBSERVED]`, `[INFERRED]`, `[UNKNOWN]`. +- **Output is the last thing.** Skill ends after the report renders. No follow-up prompts. +- **Read-only.** No `wit_update_work_item`, no `wit_add_work_item_comment`, no `teams_send_message`. Posting is the caller's job. + +## Tool naming note + +The frontmatter `tools` list uses short, unprefixed names (`wit_get_work_item`, `wit_query_by_wiql`, `wiki_search`, `teams_search_messages`). The actual tool prefix depends on which Azure DevOps MCP server and which Teams MCP server you have installed and how Claude Code mounts them. If the skill's tool calls fail because the prefix doesn't match, edit the frontmatter to add your prefix once. The skill body refers to tools by their short name throughout. If no Teams MCP is installed, Level 1 is silently skipped. + +## Search Ladder + +Investigation runs three levels top to bottom. Each level has a gate: if it produces enough evidence to write a useful report, skip the remaining levels. Datadog is not in the ladder by default; non-bug work items rarely have runtime telemetry to query, and adding it pulls in a tool the skill does not need for this archetype. + +### Setup + +Before running the levels, fetch the work item once and cache it for the rest of the skill. + +1. Identify the work-item ID from the invocation context (e.g., `12345` from a pasted URL or a parameter passed by the caller). If the calling context (such as the `azure-issue-triage` agent) has already fetched the work item and exposed the payload, reuse that payload; don't fetch again. +2. If no payload is available, call `wit_get_work_item` with the ID and `expand: "all"`. Request these fields at minimum: `System.Title`, `System.Description`, `System.State`, `System.WorkItemType`, `System.Tags`, `System.AreaPath`, `System.IterationPath`, `System.AssignedTo`, `System.CreatedBy`, `System.CreatedDate`, `System.ChangedDate`, `System.Parent`, plus the `relations` array. Add the optional fields the caller cares about (`Microsoft.VSTS.Common.AcceptanceCriteria`, `Microsoft.VSTS.Scheduling.StoryPoints`) when known. +3. Determine the archetype: use the value passed by the caller. If running standalone, map `System.WorkItemType` using the table below. If the work-item type is `Bug`, or `Issue` / `Impediment` (which the agent routes as Incident), stop and tell the caller this skill does not handle those archetypes; route to `azure-issue-investigator` instead. + + | Work-item type (Agile / Scrum / CMMI) | Archetype | Report template | + |---------------------------------------|-----------|-----------------| + | User Story (Agile) / Product Backlog Item (Scrum) / Requirement (CMMI) | User Story | Feature template | + | Feature / Epic | Feature (epic-level — note in Lead) | Feature template | + | Task | Task | Task template | + | Task tagged `spike`, or a custom `Spike` work-item type | Spike | Spike template | + +4. Cache the response as "the work-item payload" throughout the skill. + +If `wit_get_work_item` fails (auth error, work item not found, network), stop and tell the caller which call failed. Do not proceed without work-item data. + +### Level 1: Teams + +Skip entirely if no Teams MCP server is installed. + +Run 2-3 queries via `teams_search_messages`: + +1. The work-item ID (e.g., `12345` or `AB#12345`). +2. The feature, task, or spike name, or the most distinctive phrase from the title. +3. The area or system name combined with a key term from the description. + +For each relevant hit, follow the thread in full with `teams_read_thread`. + +What you are looking for: +- A prior decision that defines the scope (often labeled "decided", "approved", "agreed"). +- A linked design doc, product brief, or RFC. +- A named owner or Decider for the area. +- A prior thread that names the same problem with a different framing (the team may have a different vocabulary for the same scope). + +**Gate:** if a Teams thread contains a confirmed scope statement, an approved design link, or a clear "out of scope" decision, write the report citing that thread and skip Levels 2-3. + +### Level 2: Work Item + AzDO + Wiki + +Read the work-item payload (cached in Setup) carefully. Signals are easy to miss on a fast scan: linked design docs, mentions of related work items, vocabulary like "blocks" or "depends on" that points at sequencing constraints, the question the reporter is actually asking. + +Then search: + +- **Linked work items.** Walk every entry in the `relations` array and the `System.Parent` field. Read the related work-item titles and the most recent 1-2 comments. Note: state, assignee, the most relevant scope statement. +- **Related work items** via `wit_query_by_wiql`. Common patterns: + - `SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.TeamProject] = '' AND [System.Description] CONTAINS '' ORDER BY [System.CreatedDate] DESC` + - `SELECT [System.Id], [System.Title], [System.State] FROM WorkItems WHERE [System.TeamProject] = '' AND [System.AreaPath] UNDER '' AND [System.WorkItemType] IN ('User Story', 'Product Backlog Item', 'Task', 'Feature') ORDER BY [System.CreatedDate] DESC` + - When the work item has a parent epic or feature: `SELECT [System.Id], [System.Title] FROM WorkItemLinks WHERE Source.[System.Id] = AND [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward' MODE (Recursive)`. +- **AzDO Wiki** via `wiki_search`. Search for product briefs, design docs, ADRs, RFCs, runbooks for the area. Use the feature area, system name, or product theme as the search term. + +For each related work item, record: ID, title, state, assignee, the most relevant scope or AC finding. +For each Wiki page, record: URL and a 1-line summary of what it contains. + +**Gate:** if a product brief or design doc explicitly states the scope and acceptance criteria for this work, write the report citing that source. Skip Level 3 unless the work item also references existing code that needs orientation. + +### Level 3: Code + +Enter only when Levels 1-2 turned up nothing useful, OR the work item explicitly references existing code that needs context to size or scope the work. + +1. **Affected service or module.** When the work item names a service, module, or file path, use `Bash` (e.g., `git ls-files | grep -i 'pattern'`) or `Grep` to locate it in the repository. Note the relative path. +2. **Existing patterns.** When the work item references "the same way we do X for Y", use `Grep` to find the X pattern and `Read` the existing implementation. Record the path and a 1-line summary of the pattern. +3. **Recent changes.** Run `git log --since="2 months ago" -- ` via `Bash` to find commits in the affected area. The recent change list informs the "Where To Look" section. + +Stop when you can name: which area of the code is involved, what existing patterns to follow, and 1-3 specific files or directories the developer should open first. Do not trace full call chains; this is orientation, not implementation. + +## Evidence Model + +Every claim in the report carries one of four tags. + +| Tag | Meaning | +|-----|---------| +| `[VERIFIED]` | Directly confirmed. Read in code or in an authoritative source (design doc, ADR, work-item description) that explicitly states this. | +| `[OBSERVED]` | A pattern matches the reported scope, but reaching the conclusion required a logical step. | +| `[INFERRED]` | Logical deduction from available information. Not directly observed. | +| `[UNKNOWN]` | Cannot determine from available sources. Requires asking a person or reading docs that were not found. | + +If the finished report has more `[INFERRED]` than `[VERIFIED]` findings, the search was insufficient. Go back and search more before writing. + +Every `[UNKNOWN]` becomes either an "Open Questions" item (when the unknown blocks start of work and a person can answer it) or a "Where To Look" item (when the unknown can be resolved by reading or searching). + +## Stop Condition + +Investigation is **done** when all three are true: + +1. The Lead names what the work item asks for in one or two sentences, with the strongest evidence inline. +2. At least one source has been consulted at every search level the investigation reached. (If Level 1 closed via its gate, Levels 2-3 do not need sources. If Level 1 was skipped because no Teams MCP is installed, that is not a source gap.) +3. There are concrete next-step queries, file paths, or doc links in "Where To Look". + +If any one is missing, keep investigating. + +## Report Template + +The template differs by archetype. Read `references/report-template.md` when you reach the report-writing step. Pick the matching template based on the archetype determined in Setup. + +Section orders and definitions live in `references/report-template.md`. The three templates share five concepts: a one-line Lead, a context section (Background or Why Now or Question to Answer), a scope-or-knowledge section, an unknowns section, and a Where To Look section. + +## Adaptation Rules + +These rules adjust section emphasis without changing the section list. + +- **Found at Level 1 (Teams):** the context section leads with the Teams source and links the thread. Other context sections may be 1 line. +- **Found at Level 2 (design doc or product brief):** the context section leads with the doc URL and quotes the most relevant sentence. Same brevity allowed elsewhere. +- **Required Level 3 (code):** the Where To Look section includes code references as `path/to/file.ext` (no line numbers; this is orientation, not pinpointing). When the existing pattern is the finding, name the file in the context section. +- **Vague work item (almost no signal):** the Open Questions / Open Blockers section grows to name what the developer would need from the reporter or product owner before starting. Do not pad with prescriptive suggestions; only name genuine unknowns. + +## Writing Rules + +These apply to all text in the report. + +- No em dashes or spaced hyphens as separators. Em dashes inside parenthetical asides are fine. +- No LLM vocabulary: delve, leverage, robust, seamlessly, comprehensive, nuanced, elevate, foster, paradigm, ecosystem, holistic, innovative, synergy, empower, facilitate. +- Lead with the answer. No opener phrases. +- No trailing summaries on short sections. +- Prose over bullet lists when the content flows naturally as sentences. +- Never present unverified scope as confirmed acceptance criteria. +- Quote design docs and product briefs directly (single sentence) when paraphrasing risks losing meaning. diff --git a/azure-issue-triage/skills/azure-requirements-investigator/references/report-template.md b/azure-issue-triage/skills/azure-requirements-investigator/references/report-template.md new file mode 100644 index 0000000..fb5f6ea --- /dev/null +++ b/azure-issue-triage/skills/azure-requirements-investigator/references/report-template.md @@ -0,0 +1,148 @@ +# Report Template + +Read this when you reach the report-writing step in `azure-requirements-investigator`. Skip on earlier steps. + +The report template differs by archetype. Pick the matching template based on the archetype passed by the caller (the agent in Phase 1) or inferred from the work-item-type when running standalone (User Story / Product Backlog Item / Requirement -> Feature; Task -> Task; Spike custom type or Task tagged `spike` -> Spike). + +Each template has a fixed section list and order. Do not omit a section heading. If a section has nothing meaningful to say, write a one-line note under the heading ("Not applicable for this work item" or "Nothing prior found"). The absence is itself signal, and keeping the heading lets a reader scan for the section even when it is empty. + +## Feature Template + +Six sections. The agent routes User Story, Feature, and Epic work items into this template. A standalone invocation against any of those archetypes uses the same shape. + +### 1. Lead + +1-2 sentences. Name what is being built and the single best summary of scope. Inline evidence tag. Do not restate the work-item title. + +Example: + +> Add a per-tenant rate limit for the bulk-export endpoint, default 100 requests per hour, configurable per tenant `[VERIFIED]` from the product brief at WI #1234. + +### 2. Background + +2-4 sentences. Why this work item exists. Prior decisions, related history, links to product briefs, design docs, runbooks, related work items. Pull this from the AzDO Wiki and the linked work items discovered in Level 2. + +When the background is established in a Wiki brief or ADR, lead with the URL and quote the single most relevant sentence. Do not paraphrase across multiple decisions; name them separately. + +### 3. Requirements Found + +Concrete acceptance criteria, definition of done, success metrics, target user stories. Pull from the work item itself (including the `Microsoft.VSTS.Common.AcceptanceCriteria` field if populated), linked work items, and any Wiki spec the work item points to. + +Format as a bulleted list when there are 3 or more items. Each bullet starts with the requirement, followed by the source citation in brackets. Tag explicit gaps as `[UNKNOWN]`. + +Example: + +> - Bulk export rate limit defaults to 100 req/hour per tenant `[VERIFIED]` (WI #1234 description, paragraph 2). +> - Override via `bulk_export_rate_limit` config field, integer in requests/hour, no upper bound `[OBSERVED]` (WI #1234 comment from @alice 2026-04-15). +> - Rate limit error response shape unspecified `[UNKNOWN]`. + +### 4. Design Refs + +Links to Figma boards, design docs, mockup reviews, ADRs. One bullet per link with a one-phrase summary of what's at the link. If nothing is linked, write "None found in work item or comments." A Feature work item with no design refs is itself a triage flag, worth naming. + +### 5. Open Questions + +Genuine unknowns that need an answer before development starts. Each question is specific (not "what's the scope?"). Tag the most likely answerer if discoverable from the work-item history or the Teams search. + +Example: + +> - What error response shape should the rate limit return? Likely answerer: @bob (named in WI #1234 thread). `[UNKNOWN]` +> - Does the rate limit apply to admin-impersonated requests? `[UNKNOWN]` + +### 6. Where To Look + +2-5 tool-by-tool items. Each item: + +- Names the tool (code search, AzDO Wiki search, Teams search, work-item ID, design tool). +- Gives the exact ready-to-paste query, URL, or file path. +- Says in one phrase what a hit or miss tells you. + +Examples: + +> - **Code search:** `rg 'rate_limit' services/bulk_export/` to find existing rate-limit infrastructure. A hit identifies the pattern to follow; a miss means this is greenfield in the bulk-export service. +> - **Teams search:** "bulk export" in the platform channel to find the design discussion that led to WI #1234. A hit gives the rationale behind the 100 req/hour default. +> - **AzDO Wiki:** search `Bulk Export ADR` (full text) to find the architecture decision record. The ADR is the canonical scope source. + +## Task Template + +Five sections. + +### 1. Lead + +1-2 sentences. Name what needs to happen and the why-now. Inline evidence tag. + +Example: + +> Bump `pg` driver from 8.x to 9.x across the `payments` and `billing` services to unblock Postgres 16 upgrade `[VERIFIED]` from WI #2345. + +### 2. Why Now + +2-3 sentences. The trigger for the task: dependency upgrade unblocks something downstream, deprecation deadline, related migration, runbook execution, security advisory. + +Pull from the work item and recent Teams discussions. When a deadline or dependency is the trigger, name it specifically with date or work-item reference. + +### 3. Definition of Done Found + +Concrete completion criteria. Pull from the work item and any linked checklist or runbook. Tag explicit gaps as `[UNKNOWN]`. + +Format as a bulleted list. Each bullet is a single completion check, not a process step. + +### 4. Risks + +Anything in the affected code, config, or runbook that makes this task non-trivial. One bullet per risk. Examples: + +- Shared config touched by other teams. +- Dependency with a known breaking change between minor versions. +- Infrastructure resource with downstream consumers (database, queue, shared library). +- The task touches a runbook step that has no documented rollback. + +If no risks are found, write "No specific risks surfaced from search; standard care expected." + +### 5. Where To Look + +Same format as Feature template Section 6. + +## Spike Template + +Five sections. The agent routes any Task tagged `spike` (or a custom Spike work-item type) into this template. + +### 1. Lead + +1-2 sentences. Name the question being investigated and the time-box if known. Inline evidence tag. + +Example: + +> Spike: evaluate whether Cognito can support more than 100 IdPs per user pool. Time-boxed at 3 days `[VERIFIED]` from WI #3456. + +### 2. Question to Answer + +The specific decision or unknown the spike is meant to resolve. Quote it directly from the work item if the work item states it well; rewrite if vague. Multiple sub-questions are allowed if they are all in scope. + +Example: + +> Primary question: Does Cognito support more than 100 IdPs per user pool today? `[VERIFIED]` from WI #3456. +> Sub-questions: What is the practical performance impact of >100 IdPs? What are the migration paths if the limit is hard? + +### 3. What's Already Known + +Findings from any prior spike, design doc, related work item, or Teams thread that bear on the question. Tag each with evidence level. If nothing is known, write "Nothing prior found." (which is itself a useful signal: the spike is starting from scratch). + +### 4. What's Unknown + +The gaps that the spike needs to fill. One bullet per gap. Each phrased as a concrete check (`Does Cognito support more than 100 IdPs?`) rather than a vague topic (`scalability`). + +When the team has access to a vendor representative or a person with prior context, name them on the relevant unknown. + +### 5. Where To Look + +Same format as Feature template Section 6. For Spikes, "Where To Look" frequently includes external research (vendor docs, RFCs, comparison articles, vendor support tickets) in addition to internal sources. + +Example: + +> - **Vendor docs:** AWS Cognito IdP service quotas page (https://docs.aws.amazon.com/cognito/latest/developerguide/limits.html) to confirm the documented limit. +> - **Vendor support:** check past AWS Support cases for IdP limit-raise requests; AWS often raises soft limits. +> - **Related code:** `rg 'IdentityProvider' services/auth/` to see how many IdPs are currently configured per pool in the existing codebase. + +## Section Order + +Sections appear in the order shown for each archetype. Never omit a section heading. When a section legitimately has nothing to say, keep the heading and write the brief placeholder line described at the top of this file. If the absence is itself important context (e.g., a Feature work item with no design refs), call it out in the Lead in addition to the placeholder line in the empty section. diff --git a/azure-issue-triage/skills/azure-work-item-refiner/SKILL.md b/azure-issue-triage/skills/azure-work-item-refiner/SKILL.md new file mode 100644 index 0000000..24ba9fe --- /dev/null +++ b/azure-issue-triage/skills/azure-work-item-refiner/SKILL.md @@ -0,0 +1,175 @@ +--- +name: azure-work-item-refiner +description: "Restructures a poorly written Azure DevOps work item into a clear, self-contained document that a stranger can read cold and act on. Works on any work-item type (Bug, Incident, User Story / Feature, Task, Spike). Updates the description (System.Description) and title (System.Title) via the Azure DevOps MCP and never deletes original content. Use when the user asks to refine, rewrite, restructure, clean up, or improve an Azure DevOps work item." +metadata: + author: Taha Bikanerwala +tools: AskUserQuestion, Read, wit_get_work_item, wit_update_work_item, wit_add_work_item_comment, core_list_projects +--- + +# Azure Work Item Refiner + +Take an Azure DevOps work item that is hard to read and turn it into a document a stranger can open cold, in a year, and act on. Reorganize the content. Never delete it. Every fact in the original survives the rewrite, just placed somewhere it can be found. + +This skill modifies Azure DevOps in its default mode: it updates the work item's title and description (writing to the `System.Title` and `System.Description` fields, both submitted as a JSON Patch document via `wit_update_work_item`), and posts an optional next-steps comment when the user asks for one. **One exception:** when invoked with `Calling context: skip_preview=true.` (the `azure-issue-triage` agent does this in Phase 5), the skill operates in **read-only-return mode** and performs no Azure DevOps writes at all; it returns the refined title and description as plain text for the caller to write. See the "Calling Convention" bullets below for the full read-only-return contract. + +## Calling Convention + +The skill works two ways. When a user pastes a work-item ID or URL and asks to refine it, run end to end. When the `azure-issue-triage` agent calls this skill in Phase 5, treat the agent's already-fetched payload and investigation findings as the source data and skip the fetch step. + +- **One confirmation gate.** Always preview the rewrite before calling `wit_update_work_item`. The user must approve. **Exception:** when the prompt passed to the skill begins with `Calling context: skip_preview=true.` (the `azure-issue-triage` agent inserts this leading line in Phase 5 because the agent already captured user approval at Phase 3 and renders its own informational preview before writing), the skill operates in **read-only-return mode**: + - Do not call `AskUserQuestion` (no preview gate). + - Do not call `wit_update_work_item` (no description or title write). + - Do not call `wit_add_work_item_comment` (no Step 8 next-steps comment, even if the input would normally trigger it). + - Do not call any other Azure-DevOps-mutating MCP tool. + - The skill's only side effects are reading the cached payload and producing the refined title and description as plain-text output for the caller to consume. + - The agent (Phase 5 step 4) owns the `wit_update_work_item` write; if the user wants a Step 8 next-steps comment, they request it through a separate flow after the agent run completes. +- **Read-then-write.** Refuse to write before reading the description, comments, and (when relevant) revision history. +- **Strict superset.** The refined work item contains every fact from the original. Restructure, rewrite, and re-tag, never truncate. +- **No solution prescription.** The skill structures information. It does not invent fixes, recommend roadmap, or editorialize on causes that are not in the work item. + +## Prerequisites + +- The user provides a work-item ID (e.g., `12345`) or a work-item URL (e.g., `https://dev.azure.com///_workitems/edit/12345`). +- The Azure DevOps MCP server is configured. `core_list_projects` returns at least one project so the organization context is resolvable. +- The user has edit permission on the work item. + +If any prerequisite fails, stop and tell the user which call failed before continuing. + +## Workflow + +The workflow is eight ordered steps. Steps 1, 5, and 6 require reading the corresponding reference file when you reach that step. Do not pre-load the references at the start of the run. + +1. **Fetch** the work item: fields, comments, revision history when needed, and linked work items. +2. **Classify** the work-item archetype (Bug / Feature / Task / Incident / Spike). The archetype controls which sections appear in the refined description. +3. **Inventory** every distinct fact across the description, comments, revision history, and linked work items. Tag each fact with its information category. +4. **Rewrite** the inventory into prose, applying the rewrite principles. +5. **Apply the template** in `assets/template.md`, including only the sections the archetype calls for. +6. **Rewrite the title** so a reader on a board can decide whether to open the work item. +7. **Preview** the proposed title and description as inline markdown. Wait for the user to approve. +8. **Post a next-steps comment** only when the user asks for one. + +--- + +### Step 1: Fetch the Work Item + +Read [references/gathering-guide.md](references/gathering-guide.md) when you reach this step. It documents which fields to request, the HTML content-loss check, and the completeness gate that blocks Step 2 until comments and revision history are read. + +If the calling context (the `azure-issue-triage` agent in Phase 5) has already fetched the work item and exposes the payload, reuse it. Do not refetch. + +### Step 2: Classify the Archetype + +Use the table below. The archetype is the single most important variable in the rewrite because it picks which template sections appear in the final description. + +| Archetype | Typical Azure DevOps work-item types | What the content looks like | +|-----------|---------------------------------------|------------------------------| +| **Bug** | Bug | A user-visible symptom, an error, broken behavior, or unexpected output. May include reproduction steps. | +| **Feature** | User Story (Agile), Product Backlog Item (Scrum), Requirement (CMMI), Feature, Epic | A user need or business goal, acceptance criteria, design specs, links to product briefs. | +| **Task** | Task | Operational work: cleanup, configuration, migration, dependency upgrade, runbook execution. | +| **Incident** | Issue (Agile), Impediment (Scrum), Issue (CMMI). Some teams also use Bug + a `incident` tag. | Production impact, blast radius, timeline, mitigation steps, post-mortem context. | +| **Spike** | Task or User Story tagged `spike`, or a custom Spike work-item type. | Open questions to resolve, exploration scope, proof-of-concept boundaries, preliminary findings. | + +**When the work-item type and the content disagree, trust the content.** A work item typed `Bug` whose body is acceptance criteria and a Figma link is a Feature. A work item typed `Task` describing user-visible breakage is a Bug. The content drives the template, not the work-item type field. + +Hold the archetype as working context. Do not surface it in the preview unless the user asks. + +### Step 3: Inventory the Information + +Catalog every distinct fact found in the description, every comment, the revision history (if read), and any linked work items that add scope. Read [references/classification-guide.md](references/classification-guide.md) when you reach this step. It defines the seven information categories and the verified-vs-unverified flag every item carries. + +### Step 4: Rewrite + +Apply the rewrite principles from `references/classification-guide.md` (already loaded in Step 3). The principles cover symptoms-over-solutions, evidence-over-claims, prose-over-tables, and the rules for surfacing decisions buried in comment threads. + +### Step 5: Apply the Template + +Structure the rewritten content using `assets/template.md`. Read it now, alongside [references/azure-html-formatting.md](references/azure-html-formatting.md) for the markdown-to-HTML safety rules. + +Three rules are non-negotiable: + +- **Pick sections by archetype.** The template ships with an archetype-to-sections map. Include only the sections that map says belong to the current archetype. +- **Skip empty sections.** A section header with no content under it is noise. Omit it. +- **Fold raw metadata into the body.** Source-system blocks (intake forms, support escalation tables, Zendesk dumps, customer-feedback exports) get their facts extracted and placed in the appropriate template section. Do not preserve the raw block verbatim. The only time an Original Metadata section is appropriate is when a fact is genuinely unclassifiable and has no other home. + +### Step 6: Rewrite the Title + +Read [references/title-guide.md](references/title-guide.md) when you reach this step. It documents the title pattern, the archetype-by-archetype examples, and the character budget. + +### Step 7: Preview and Confirm + +Preview before posting. The preview is the user's only chance to catch a mistake before the work item is overwritten. + +**Render the preview as inline markdown.** The refined description contains its own fenced code blocks (errors, queries, JSON). Wrapping the whole preview in an outer code fence breaks every inner block. Use this exact layout: + +1. A horizontal rule. +2. The new title on its own line, formatted as bold `Title:` followed by the rewritten title text inside an inline-code span. "Title" here means the user-facing label and the value that will land in the `System.Title` field. The literal markdown to emit is shown below. +3. A blank line. +4. The full refined description rendered as plain markdown, no outer fence. The agent body converts this markdown to HTML before writing; see `references/azure-html-formatting.md` for the safe-HTML subset and conversion rules. +5. A horizontal rule. +6. A short prompt asking the user to approve or request changes. + +The title line in step 2 looks like this when emitted as markdown: + +```markdown +**Title:** `{the rewritten title}` +``` + +Do not pad the preview with workflow notes (`Archetype:`, `HTML warning:`, `Previous state:`). The preview is what will appear on the work item. If the rewrite carries a real risk of losing HTML-only content (rich attachments, embedded videos, complex tables, in-line mentions, color-tinted callouts), say so in one sentence outside the preview. Do not warn for cosmetic-only losses. + +After the user reviews: + +- **Approved.** Call `wit_update_work_item` once with a JSON Patch document setting `System.Title` and `System.Description`. The patch's `System.Description` value is HTML; convert the markdown rewrite to HTML using the rules in `references/azure-html-formatting.md`. Never send raw markdown into `System.Description`: the AzDO API stores it verbatim and renders the literal markdown characters on the work item. +- **Changes requested.** Revise and re-preview. Do not call `wit_update_work_item` until the user explicitly approves. + +### Step 8: Post a Next-Steps Comment + +Skip this step unless the user asks for it. + +When asked, post via `wit_add_work_item_comment`. Comments in Azure DevOps are stored as HTML; convert your draft from markdown to safe HTML using the rules in `references/azure-html-formatting.md` before submitting. + +Build the comment with these elements: + +- An `

` heading whose text is `Next Steps (YYYY-MM-DD)`. Substitute today's date in `YYYY-MM-DD` form. Use parentheses for the date so the heading does not need a separator. +- An `
    ` list containing one `
  1. ` per action. +- Every action names an owner or team (as plain text, not an `@mention`, unless the user explicitly approved tagging that account). +- Use concrete verbs. Write `Verify token rotation schedule with Platform team` rather than `Look into auth`. + +The skeleton below is the structure to build. Substitute the real date for `YYYY-MM-DD`. + +```html +

    Next Steps (YYYY-MM-DD)

    +
      +
    1. Verify token rotation schedule with Platform team.
    2. +
    3. Page on-call if customer impact persists past 14:00 UTC.
    4. +
    +``` + +Comment body actions belong here, not in the description. The description records what is known and unknown. The comment records what happens next. + +--- + +## Anti-Patterns + +These are hard rules. Each one prevents a failure mode that has been observed on real refinements. + +1. **Never present unverified analysis as confirmed root cause.** Frame it as a working hypothesis in the Working Hypotheses section, in plain prose. No disclaimer block, no warning panel. +2. **Never inject solutions that are not in the original work item.** If the original contains evidence pointing toward a fix, surface the evidence. Drop the solution. +3. **Never add roadmap or tech-debt suggestions.** A work-item description is a record. It is not a forum for "we should also...". +4. **Never replace the description with less information.** The refined version is a strict superset of the original. The amount of unique information cannot decrease. +5. **Never lose investigation artifacts.** Customer IDs, log links, query results, error strings, attachment URLs, screenshot embeds, video links: every one survives. +6. **Never emit raw markdown into `System.Description`.** The API stores HTML; it does not run a markdown-to-HTML conversion. Convert before writing. +7. **Never inject `