Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions plugins/jira/commands/create.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,36 @@ Prompt for missing required information based on issue type:
3. Business requirements (customer impact, regulatory drivers, justification)
4. Affected packages and components (teams, operators, component mapping)

### ✅ Phase 5.5: Summary Validation

Before security validation, validate the summary format to catch common mistakes:

**Check for anti-patterns:**
1. Summary starts with "As a" (user story format belongs in description)
2. Summary contains "I want" or "so that" (belongs in description)
3. Summary exceeds 100 characters (likely too long, may be full user story)

**Action if anti-pattern detected:**
1. Detect that user put full user story in summary field
2. Extract the key action/feature from the summary
3. Generate a concise alternative (5-10 words)
4. Prompt user for confirmation:
```
The summary looks like a full user story. Summaries should be concise titles.

Current: "As a cluster admin, I want to configure ImageTagMirrorSet in HostedCluster CRs so that I can enable tag-based image proxying"

Suggested: "Enable ImageTagMirrorSet configuration in HostedCluster CRs"

Use the suggested summary? (yes/no/edit)
```

5. If user says yes, use suggested summary
6. If user says edit, prompt for their preferred summary
7. If user says no, use their original summary (but warn it may be truncated in Jira)

**Note:** This validation should happen BEFORE creating the issue, to avoid having to update the summary afterward.
Comment on lines +167 to +195
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix markdown linting: Add language identifier to example output block.

The code block at lines 181-189 lacks a language specification. Per MD040, fenced code blocks should declare their language.

Apply this diff:

- ```
+ ```text
  The summary looks like a full user story. Summaries should be concise titles.
  
  Current: "As a cluster admin, I want to configure ImageTagMirrorSet in HostedCluster CRs so that I can enable tag-based image proxying"
  
  Suggested: "Enable ImageTagMirrorSet configuration in HostedCluster CRs"
  
  Use the suggested summary? (yes/no/edit)
- ```
+ ```

Content-wise: Phase 5.5 is well-designed. The anti-pattern heuristics (checking for "As a", "I want", "so that", and 100-char limit) effectively catch the common mistake of putting full user stories in the summary field. The user workflow (detect → suggest → prompt) is intuitive.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

173-173: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In plugins/jira/commands/create.md around lines 167 to 195, the fenced code
block example (lines ~181-189) is missing a language identifier which violates
MD040; update the opening triple backticks to include a language (use "text")
and ensure the closing triple backticks remain; no other content changes
required.


### 🔒 Phase 6: Security Validation

Scan all content (summary, description, comments) for sensitive data:
Expand Down Expand Up @@ -202,6 +232,8 @@ Use the `mcp__atlassian__jira_create_issue` MCP tool with collected parameters.

The MCP tool parameters come from the combined guidance of type-specific, project-specific, and team-specific skills, with universal requirements always applied.

**Note:** Project-specific skills (e.g., CNTRLPLANE) may implement fallback strategies for handling creation failures (such as epic linking). Refer to the project-specific skill documentation for these strategies.

### 📤 Phase 8: Return Result

Display to user:
Expand Down Expand Up @@ -428,6 +460,47 @@ Would you like to edit the description?
- **"Permission denied"** → User may lack permissions, suggest contacting admin
- **"Issue type not available"** → Project may not support this issue type

### Epic Link Creation Failure

**Scenario:** Story/task creation fails when including epic link field.

**Action:**
Refer to project-specific skills for epic linking fallback strategies:
- **CNTRLPLANE:** See CNTRLPLANE skill "Epic Linking Implementation Strategy" section
- **Other projects:** Consult project-specific skill documentation

**General pattern:**
1. Detect error related to linking (error contains "epic", "parent", "link", or "customfield")
2. Check project-specific skill for recommended fallback approach
3. Typically: Create without link, then link via update
4. Inform user of outcome

### Field Format Error

**Scenario:** Field provided in wrong format (e.g., Target Version as string instead of array).

**Common field format errors:**

1. **Target Version format**
- ❌ Wrong: `"customfield_12319940": "openshift-4.21"`
- ✅ Correct: `"customfield_12319940": [{"id": "12448830"}]`
- **Action:** Fetch version ID using `mcp__atlassian__jira_get_project_versions`, convert to correct format

2. **Epic Link format**
- ❌ Wrong: `"parent": {"key": "EPIC-123"}` (for stories)
- ✅ Correct: `"customfield_12311140": "EPIC-123"` (string, not object)
- **Action:** Convert to correct format and retry

3. **Component format**
- ❌ Wrong: `"components": "ComponentName"`
- ✅ Correct: `"components": ["ComponentName"]` (array) or just `"ComponentName"` (MCP accepts both)
- **Action:** Ensure consistent format

**Detection:**
- Parse error message for field names
- Check skill documentation for correct format
- Automatically convert and retry when possible

## Best Practices

1. **Use descriptive summaries:** Include relevant keywords for context and auto-detection
Expand Down
228 changes: 194 additions & 34 deletions plugins/jira/skills/cntrlplane/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,150 @@ This skill is automatically invoked by the `/jira:create` command when the proje
**Note:** Universal requirements (Security Level: Red Hat Employee, Labels: ai-generated-jira) are defined in the `/jira:create` command and automatically applied to all tickets.

### Target Version (customfield_12319940)
**Purpose:** Target release version for the feature/story/task

**Common default:** `openshift-4.21` (current development release)
**Status:** OPTIONAL (many issues in CNTRLPLANE have null target version)

**Override:** Teams may specify different versions based on their roadmap:
- `openshift-4.20` (maintenance release)
- `openshift-4.22` (future release)
- `openshift-4.23` (future release)
- Or team-specific version schemes
**Recommendation:** **Omit this field** unless specifically required by the team or user explicitly requests it.

**If target version must be set:**

1. **First, fetch available versions:**
```python
versions = mcp__atlassian__jira_get_project_versions(project_key="CNTRLPLANE")
```

2. **Find the version ID** for the desired version (e.g., "openshift-4.21" has id "12448830")

3. **Use correct MCP format** (array of version objects with ID):
```python
"customfield_12319940": [{"id": "12448830"}] # openshift-4.21
```

**Common version IDs:**
- `openshift-4.21`: `{"id": "12448830"}`
- `openshift-4.20`: `{"id": "12447110"}`
- `openshift-4.22`: `{"id": "12448831"}`

**IMPORTANT:** Do NOT use string format like `"openshift-4.21"` - this will fail. Must use array with version ID.

**Never set:**
- Fix Version/s (`fixVersions`) - This is managed by the release team

### Version Override Handling

When user specifies a different version:
1. Accept the version as provided
2. Validate version exists using MCP tool `jira_get_project_versions` if needed
If user specifies a version:
1. Fetch available versions using `mcp__atlassian__jira_get_project_versions`
2. Find the matching version ID
3. If version doesn't exist, suggest closest match or ask user to confirm
4. Use array format with version ID: `[{"id": "VERSION_ID"}]`

## Epic Link Requirements

**⚠️ CRITICAL:** To link a story to an epic in CNTRLPLANE, you **MUST** use the Epic Link custom field, NOT the `parent` field.

### Epic Link Field (customfield_12311140)

**Field Details:**
- **Field Name:** Epic Link
- **Custom Field ID:** `customfield_12311140`
- **MCP Parameter:** `additional_fields.customfield_12311140`
- **Value Format:** Epic key as string (e.g., `"CNTRLPLANE-123"`)
- **Used For:** Linking stories to epics

**IMPORTANT:** Do NOT use `additional_fields.parent` for epic-story relationships. The `parent` field has different semantics and will cause creation to fail.

### MCP Format for Epic Link

```python
additional_fields={
"customfield_12311140": "CNTRLPLANE-123", # Epic Link (use actual epic key)
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"}
}
```

### Epic Linking Implementation Strategy

When the `--parent` flag is provided for a story/task, use this implementation strategy:

#### Pre-Validation (Do This First)

Before attempting to create the issue:
1. Verify the parent epic exists using `mcp__atlassian__jira_get_issue`
2. If epic doesn't exist, prompt user:
```
Epic {epic_key} not found. Options:
1. Proceed without epic link
2. Specify different epic
3. Cancel creation

What would you like to do?
```
3. Only proceed if epic is valid or user chooses to proceed without link

#### Preferred Approach: Include Epic Link in Creation

Attempt to create the issue with Epic Link included:
```python
mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
summary="<story title>",
issue_type="Story",
description="<description>",
components="<component>",
additional_fields={
"customfield_12311140": "<epic-key>", # Epic Link (e.g., "CNTRLPLANE-456")
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"}
}
)
```

#### Fallback Strategy (If Creation Fails)

If creation fails with an error related to epic linking:
1. Detect error contains keywords: "epic", "parent", "customfield", or "link"
2. Inform user: "Epic link failed during creation, using fallback strategy..."
3. Create issue WITHOUT the epic link:
```python
story = mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
summary="<story title>",
issue_type="Story",
description="<description>",
components="<component>",
additional_fields={
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"}
}
)
```
4. If creation succeeds, link to epic via update:
```python
mcp__atlassian__jira_update_issue(
issue_key=story["key"],
fields={},
additional_fields={
"customfield_12311140": "<epic-key>"
}
)
```
5. Inform user of success:
```
Created: CNTRLPLANE-XXX
Linked to epic: <epic-key> ✓
Title: <story title>
URL: https://issues.redhat.com/browse/CNTRLPLANE-XXX
```

#### If Fallback Also Fails

If the update call to add Epic Link also fails:
```
Story created: CNTRLPLANE-XXX
⚠️ Automatic epic linking failed. Please link manually in Jira.
URL: https://issues.redhat.com/browse/CNTRLPLANE-XXX
```
Comment on lines +71 to +177
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Epic Linking Implementation Strategy is comprehensive and addresses PR objective well.

This section provides excellent, practical guidance on Epic Link handling:

Strengths:

  • Clear distinction: Custom field customfield_12311140 (string) vs parent field (object) — eliminates common mistake
  • Pre-validation: Defensive check that epic exists before attempting creation (lines 100-114)
  • Three-tier approach: Try with Epic Link → Fallback without link then update → Manual linking if needed
  • Concrete code examples: Shows the exact MCP calls for each path
  • Error messaging: User-friendly fallback notifications (lines 162-177)

Alignment: Integrates well with Phase 5.5 and Epic Link Creation Failure guidance in create.md

Minor markdown linting: Code blocks at lines 88, 118, 139, 153 should have language identifiers per MD040:

- ```python
+ ```python
  mcp__atlassian__jira_create_issue(

(Verify these blocks have language identifiers added if not already present)

Content quality is high and directly addresses the PR objective to document Epic Link implementation strategy.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

105-105: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


163-163: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


173-173: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In plugins/jira/skills/cntrlplane/SKILL.md around lines 71 to 177, several
fenced code blocks (notably at lines ~88, ~118, ~139, ~153) are missing language
identifiers which triggers MD040; update each triple-backtick fence to include
the "python" language tag (e.g., change ``` to ```python) so the blocks are
correctly marked and linting passes, then run the markdown linter to confirm no
MD040 warnings remain.


## Component Requirements

Expand Down Expand Up @@ -91,15 +216,31 @@ Some teams require specific components, while others do not. The CNTRLPLANE skil

### For CNTRLPLANE Stories

**Basic story (no epic link):**
```python
mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
summary="<concise story title>", # NOT full user story format
issue_type="Story",
description="<formatted description with full user story and AC>",
components="<component name>", # if required by team
additional_fields={
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"}
}
)
```

**Story linked to epic:**
```python
mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
summary="<story summary>",
summary="<concise story title>", # NOT full user story format
issue_type="Story",
description="<formatted description with AC>",
description="<formatted description with full user story and AC>",
components="<component name>", # if required by team
additional_fields={
"customfield_12319940": "openshift-4.21", # target version (default)
"customfield_12311140": "<epic-key>", # Epic Link (e.g., "CNTRLPLANE-456")
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"}
}
Expand All @@ -108,19 +249,35 @@ mcp__atlassian__jira_create_issue(

### For CNTRLPLANE Epics

**Basic epic (no parent feature):**
```python
mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
summary="<epic summary>",
summary="<concise epic title>",
issue_type="Epic",
description="<epic description with scope and AC>",
components="<component name>", # if required
additional_fields={
"customfield_12319940": "openshift-4.21",
"customfield_epicname": "<epic name>", # required, same as summary
"customfield_12311141": "<epic name>", # required, same as summary
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"}
}
)
```

**Epic linked to parent feature:**
```python
mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
summary="<concise epic title>",
issue_type="Epic",
description="<epic description with scope and AC>",
components="<component name>", # if required
additional_fields={
"customfield_12311141": "<epic name>", # required, same as summary
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"},
"parent": {"key": "CNTRLPLANE-123"} # if --parent specified
"parent": {"key": "CNTRLPLANE-123"} # parent feature link
}
)
```
Expand All @@ -130,20 +287,21 @@ mcp__atlassian__jira_create_issue(
```python
mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
summary="<feature summary>",
summary="<concise feature title>",
issue_type="Feature",
description="<feature description with market problem and success criteria>",
components="<component name>", # if required
additional_fields={
"customfield_12319940": "openshift-4.21",
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"}
# Target version is optional - omit unless specifically required
}
)
```

### For CNTRLPLANE Tasks

**Task linked to epic (via Epic Link):**
```python
mcp__atlassian__jira_create_issue(
project_key="CNTRLPLANE",
Expand All @@ -152,28 +310,30 @@ mcp__atlassian__jira_create_issue(
description="<task description with what/why/AC>",
components="<component name>", # if required
additional_fields={
"customfield_12319940": "openshift-4.21",
"customfield_12311140": "CNTRLPLANE-456", # Epic Link (if linking to epic)
"labels": ["ai-generated-jira"],
"security": {"name": "Red Hat Employee"},
"parent": {"key": "CNTRLPLANE-456"} # if --parent specified
"security": {"name": "Red Hat Employee"}
}
)
```

**Note:** If you need to link a task to a parent story, use Epic Link field (`customfield_12311140`) with the story key.

### Field Mapping Reference

| Requirement | MCP Parameter | Value |
|-------------|---------------|-------|
| Project | `project_key` | `"CNTRLPLANE"` |
| Issue Type | `issue_type` | `"Story"`, `"Epic"`, `"Feature"`, `"Task"` |
| Summary | `summary` | User-provided text |
| Description | `description` | Formatted template content |
| Component | `components` | Team-specific (optional) |
| Target Version | `additional_fields.customfield_12319940` | `"openshift-4.21"` (default) |
| Labels | `additional_fields.labels` | `["ai-generated-jira"]` (required) |
| Security Level | `additional_fields.security` | `{"name": "Red Hat Employee"}` (required) |
| Parent Link | `additional_fields.parent` | `{"key": "PARENT-123"}` |
| Epic Name | `additional_fields.customfield_epicname` | Same as summary (epics only) |
| Requirement | MCP Parameter | Value | Required? |
|-------------|---------------|-------|-----------|
| Project | `project_key` | `"CNTRLPLANE"` | Yes |
| Issue Type | `issue_type` | `"Story"`, `"Epic"`, `"Feature"`, `"Task"` | Yes |
| Summary | `summary` | Concise title (5-10 words), NOT full user story | Yes |
| Description | `description` | Formatted template (contains full user story) | Yes |
| Component | `components` | Team-specific component name | Varies by team |
| Target Version | `additional_fields.customfield_12319940` | Array: `[{"id": "12448830"}]` **Recommend omitting** | No |
| Labels | `additional_fields.labels` | `["ai-generated-jira"]` | Yes |
| Security Level | `additional_fields.security` | `{"name": "Red Hat Employee"}` | Yes |
| Epic Link (stories→epics) | `additional_fields.customfield_12311140` | Epic key as string: `"CNTRLPLANE-123"` | No |
| Epic Name (epics only) | `additional_fields.customfield_epicname` | Same as summary | Yes (epics) |
| Parent Link (epics→features) | `additional_fields.parent` | `{"key": "FEATURE-123"}` | No |

## Interactive Prompts

Expand Down
Loading