From sdlc-workflow
Implement a Jira task by reading its structured description, modifying code, running tests, and updating Jira.
How this skill is triggered — by the user, by Claude, or both
Slash command
/sdlc-workflow:implement-taskThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are an AI implementation assistant. You take a Jira task with a structured description and implement it.
You are an AI implementation assistant. You take a Jira task with a structured description and implement it.
Before proceeding, read the project's CLAUDE.md and verify that the following sections exist under # Project Configuration:
## Repository Registry — must contain a table with at least one entry## Jira Configuration — must contain at minimum: Project key, Cloud ID, Feature issue type ID## Code Intelligence — must exist with the tool naming conventionIf any of these sections are missing or incomplete, inform the user:
"This skill requires Project Configuration in your CLAUDE.md. Please run
/setupfirst to configure your project, then re-run this skill."
Stop execution immediately. Do not attempt to gather the missing information or proceed without it.
Before attempting any JIRA operations (Steps 1, 2, 3, 11), determine the access method.
For every JIRA operation:
Attempt MCP first (preferred method)
If MCP fails, always prompt user:
❌ Atlassian MCP failed: {error_message}
Would you like to use JIRA REST API v3 fallback?
Options:
1. Yes - Use REST API (requires credentials)
2. No - Skip this JIRA operation
3. Retry - I'll fix MCP configuration and retry
Choose (1/2/3):
If "1. Yes": Check CLAUDE.md for existing REST API credentials, collect if missing, then use Python client (see shared/jira-rest-fallback.md)
If "2. No": Skip the JIRA operation and inform user
If "3. Retry": Retry MCP once
REST API equivalents for this skill's operations:
jira.get_issue(id) → python3 scripts/jira-client.py get_issue <id> --fields "*all"jira.user_info() → python3 scripts/jira-client.py get_user_infojira.edit_issue(id, assignee=accountId) → python3 scripts/jira-client.py update_issue <id> --fields-json '{"assignee": {"id": "<accountId>"}}'jira.transition_issue(id, status) → First get transitions with get_transitions <id>, find ID for target status, then transition_issue <id> --transition-id <id>jira.update_issue(id, fields) → python3 scripts/jira-client.py update_issue <id> --fields-json '<json>'jira.add_comment(id, text) → python3 scripts/jira-client.py add_comment <id> --comment-md "<text>"Exception for Bash tool: When using REST API fallback, this skill may use bash -c "python3 scripts/jira-client.py <command>" for JIRA operations only.
Refer to shared/jira-rest-fallback.md for complete implementation details.
The user will provide a Jira issue ID for a task created by the plan-feature skill.
Example:
/implement-task PROJ-231
Every comment posted to Jira by this skill MUST end with the following footnote, separated from the main content by a horizontal rule.
Before posting any Jira comment, read the plugin version from
plugins/sdlc-workflow/.claude-plugin/plugin.json and extract the version field.
Use this value as {version} in the footer below.
Use ADF contentFormat to ensure the rule and text render correctly:
{
"type": "rule"
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "This comment was AI-generated by "
},
{
"type": "text",
"text": "sdlc-workflow/implement-task",
"marks": [
{
"type": "link",
"attrs": {
"href": "https://github.com/mrizzi/sdlc-plugins"
}
}
]
},
{
"type": "text",
"text": " v{version}."
}
]
}
Append these two nodes at the end of the ADF document's content array.
Use:
jira.get_issue()
Parse the structured description expecting these sections:
Also capture the issue's webUrl field from the API response (e.g. https://redhat.atlassian.net/browse/PROJ-231). This URL will be used later to create a clickable link in the PR description.
If the task description contains a Target PR section, extract the PR URL. Parse the
URL to extract owner, repo, and pr-number from the pattern
https://github.com/<owner>/<repo>/pull/<pr-number>. Store these values for use in
Steps 5, 10, and 11.
When Target PR is present, the task is a review feedback fix — the implementation adds commits to the existing PR branch instead of creating a new branch and PR.
If any required section is missing or the description doesn't follow the template, stop and ask the user for clarification.
Look up the GitHub Issue custom field ID from the project's Jira Configuration
section in CLAUDE.md (the field is listed as GitHub Issue custom field: <field-id>).
owner, repo, and
number from the pattern https://github.com/<owner>/<repo>/issues/<number>.
Store the parsed reference as <owner>/<repo>#<number> for use in Step 10.If the task has Dependencies, check each one:
jira.get_issue()
Verify status is Done or equivalent. If not, stop and inform the user.
Transition the Jira issue to indicate work has started, and assign it to the current user:
jira.user_info()
jira.edit_issue(, assignee=)
jira.transition_issue → In Progress
This ensures both ownership and status are reflected in Jira as soon as implementation begins.
Inspect the files listed in Files to Modify and Files to Create locations using the dedicated Serena instance for the task's repository. Look up the correct Serena Instance name in the Repository Registry section of the project's CLAUDE.md.
Tools are called as mcp__<serena-instance>__<tool>, where <serena-instance> is
the instance name from the Repository Registry.
get_symbols_overview on files to modify to see
their structure (classes, functions, types) without reading the entire file.find_symbol with include_body=true to read the
specific functions, structs, or components you need to understand or change.find_referencing_symbols on any symbol you plan
to modify to identify all callers and ensure your changes won't break them.search_for_pattern for configuration, string literals,
or patterns not captured as symbols.get_symbols_overview on 2–3 siblings to understand their structure and patterns, or
Read/Glob if Serena is unavailable.Note: Check the Code Intelligence section of the project's CLAUDE.md for per-instance limitations (e.g., some language servers may not support certain operations). Adapt your tool usage accordingly.
Fallback: if no Serena instance is available for the repository, use Read, Grep, and Glob tools directly.
Identify documentation files related to the code being modified. Look for:
CONVENTIONS.md at the repository rootRecord these files for use during documentation-impact evaluation in Step 6 and the documentation-currency check in Step 9.
Goals:
Check for a CONVENTIONS.md file at the repository root (using list_dir, search_for_pattern,
or Glob). If present, read it and follow its conventions throughout implementation. This includes
naming rules, directory structure for new files, code patterns, and test conventions.
This step is optional — if CONVENTIONS.md does not exist, proceed normally.
When reading CONVENTIONS.md, search for a section that lists CI check commands — look for
headings or labels such as "CI checks", "All CI checks", "Linting", "Pre-commit checks",
"Verification", or similar. Extract every command listed in that section and record them
for use in Step 9's CI verification sub-step.
Also look for any commands that generate code artifacts (e.g., OpenAPI spec generation, code generation, schema generation). Record these separately — they may produce file changes that need to be committed alongside the implementation.
If no CI check section is found in CONVENTIONS.md, proceed normally — Step 9 will
fall back to standard build/lint checks.
After inspecting the files to modify, analyze established conventions in the surrounding code. This complements the CONVENTIONS.md lookup: while CONVENTIONS.md provides explicit project-level conventions, this analysis discovers implicit conventions from sibling code.
For each file being modified or created:
get_symbols_overview on 2–3 siblings to understand their structure, or Read/Glob
if Serena is unavailable.If a convention conflict is detected — the task description or Implementation Notes contradict an established convention — flag it to the user and ask for guidance before proceeding with implementation.
Example output:
Discovered conventions (from sibling analysis):
- Error handling: All handlers in
src/handlers/useResult<T, AppError>with.context()for wrapping- Naming: Service methods follow
verb_nounpattern (e.g.,get_advisory,create_sbom)- Options: All parsers accept an
Optionsstruct as the last parameter
When the task includes a Test Requirements section, extend the convention conformance analysis to cover test files. This ensures new tests follow the same assertion patterns and structural conventions used throughout the test suite.
For each test file being created or modified:
get_symbols_overview on 2–3 sibling test files, or
Read/Glob if Serena is unavailable.assert_eq! vs custom matchers, expect().toBe() vs assert)test_<action>_<scenario>, it('should ...'))@ParameterizedTest in JUnit 5,
forEach/data arrays in Mocha/Jest, @pytest.mark.parametrize in pytest,
table-driven patterns in Go, #[rstest]/#[case] in Rust)Example output:
Discovered test conventions (from sibling test analysis):
- Assertion style: All endpoint tests in
tests/api/useassert_eq!(resp.status(), StatusCode::OK)followed by body deserialization- Response validation: List endpoint tests validate
total_count,items.len(), and at least one item's key fields- Error cases: All endpoint tests include a 404 test with
assert_eq!(resp.status(), StatusCode::NOT_FOUND)- Test naming: Tests follow
test_<endpoint>_<scenario>pattern (e.g.,test_list_advisories_filtered)- Parameterized tests: Sibling tests use
#[rstest]with#[case]for multi-ecosystem parsing tests (e.g.,test_parse_sbomintests/parser/)
Default flow (no Target PR):
Create a feature branch named after the Jira issue:
git checkout -b
Target PR flow:
When Target PR is present (parsed in Step 1), check out the existing PR branch instead of creating a new one:
gh pr view <pr-number> --json headRefName -R <owner/repo>
git checkout <branch-name>
git pull
This ensures the fix commits are added to the existing PR branch.
The Description section is your primary specification — implement exactly what it describes. Use Files to Modify, Files to Create, and API Changes as your working scope. Follow the Implementation Notes for patterns and code references on how to implement the changes.
Reuse first: Before writing new logic, check whether the Implementation Notes list reusable code (utilities, helpers, shared modules). If they do, use or extend the existing code. If you discover additional reusable code during implementation that was not listed, prefer reusing it over creating duplicated logic.
Follow conventions: Apply the conventions discovered during Step 4's convention conformance analysis. When writing new code, match the patterns found in sibling files rather than inventing new approaches. If any Implementation Notes or task instructions conflict with a discovered convention, follow the guidance obtained from the user during Step 4.
Use the dedicated Serena instance for the task's repository (look up the instance name in the project's Repository Registry):
replace_symbol_body — rewrite an entire function, method, struct, or componentinsert_after_symbol / insert_before_symbol — add new code relative to existing symbolsrename_symbol — rename a symbol and automatically update all referencesUse Edit/Write tools for non-code files, config files, or when Serena is unavailable.
For each file in Files to Modify:
get_symbols_overview + find_symbol to read selectively)For each file in Files to Create:
For API Changes:
When the task involves writing manual REST calls — HTTP requests written directly using
fetch(), axios, or similar rather than an auto-generated API client — verify the
endpoint contract against the backend repository before writing the call.
/api/v2/sboms)fetch(), axios, or HTTP client usagemcp__<serena-instance>__<tool>.items[0] for "latest",
items[items.length - 1] for "oldest"), verify the backend's actual sort order
before relying on positional selection.
response.data[0]),
.first() / .last() calls, destructuring the first element (const [latest] = items),
or comments indicating "newest", "most recent", "latest".ORDER BY clauses, .sort() calls, or sort parameters in
the query builder. Check whether a default sort is applied when no explicit sort
parameter is provided by the caller.items[0] expecting the
newest item, verify the backend sorts by created_at DESC (or equivalent), not
created_at ASC.assessments[0] expecting newest")ORDER BY created_at ASC — oldest first")If no Serena instance is available for the backend repository, use Grep, Glob, and Read on the backend repo to perform the same verification.
Example output:
Cross-repo API verification results:
GET /api/v2/sboms— path ✓, method ✓, response shape ✓ (matchesSbomSummaryinmodules/fundamental/src/sbom/model/summary.rs)DELETE /api/v2/sbom/{id}— path ✗ — MISMATCH (backend uses/api/v2/sboms/{id}with trailing 's', seemodules/fundamental/src/sbom/endpoints/mod.rs:48)GET /api/v2/risk-assessment/group/{groupId}— sort order ✗ — MISMATCH (frontend picksassessments[0]expecting newest, but backend returnsORDER BY created_at ASC— oldest first, seemodules/risk/src/assessment/endpoints/mod.rs:92)
After implementing code changes, verify the following quality practices:
After implementing code changes, evaluate whether documentation needs updating:
Implement the tests described in Test Requirements.
Follow test conventions: Apply the test conventions discovered during Step 4's test convention analysis. When writing new tests, match the assertion patterns, response validation style, error case coverage, and naming conventions found in sibling test files rather than inventing new approaches.
Prefer value-based assertions over length-only checks: When verifying collections or response data, assert on the actual values — not just the count. Assert on specific items or key fields so that test failures reveal what changed, not just how many. Length checks alone hide regressions behind a passing count and prevent subsequent assertions from running.
Prefer parameterized tests for repetitive cases: When multiple test cases exercise
the same behavior with different inputs and expected outputs, use the project's
parameterized test mechanism instead of writing individual test functions for each case.
Apply the Meszaros heuristic as the decision boundary: parameterize when tests share the
same algorithm (setup, action, assertion structure) with different data; use individual
tests when behavior, setup, or assertions differ between cases. If the test body would
need conditionals to handle parameter variations, use separate tests instead. Common
mechanisms include JUnit 5 @ParameterizedTest, Mocha/Jest forEach/data arrays,
pytest @pytest.mark.parametrize, Go table-driven tests, and Rust rstest. However,
if the sibling test analysis in Step 4 shows the project does not use parameterized
tests, do not introduce them — follow the project's existing test patterns instead.
Document every test function: Add a documentation comment before every test function
explaining what it verifies — a single line using the language's doc comment convention
(e.g., /// in Rust, /** */ in Java/TypeScript, """ docstring in Python). This applies
regardless of whether sibling tests have documentation; AI-generated tests introduce this
as a new standard that overrides the "Follow test conventions" guidance above for
documentation specifically.
For non-trivial tests — those with distinct setup, action, and assertion phases — also add
given-when-then section comments (// Given, // When, // Then) inside the test body to
make the structure navigable at a glance. A test is trivial (skip given-when-then) when it
has a single assertion with no distinct setup phase.
Example (Rust):
/// Verifies that an SBOM with no dependencies produces an empty risk score. #[test] fn test_empty_sbom_risk_score() { // Given an SBOM with no dependencies let sbom = create_test_sbom(vec![]); // When calculating the risk score let score = calculate_risk(&sbom); // Then the score should be zero assert_eq!(score, RiskScore::default()); }The doc comment convention varies by language — use
///for Rust,/** */for Java/TypeScript,"""docstring"""for Python,//doc comments for Go, etc.
Run tests to verify:
cargo test (for Rust) npm test (for TypeScript)
Fix any failures before proceeding.
Go through each Acceptance Criterion and verify it is satisfied. If any criterion cannot be met, stop and explain to the user.
Before committing, verify that all changes are in scope and free of common errors.
git diff --name-only to list all modified and created files.The scope containment check above uses git diff --name-only, which only lists
tracked file changes. Untracked files created during implementation — especially
those referenced by code via compile-time includes (include_str!, include_bytes!),
static imports, or configuration references — would be silently omitted from the
commit.
git status --short and extract entries prefixed
with ?? (untracked files).git diff --name-only output). This focuses the check on directories
where implementation work occurred, ignoring unrelated untracked files elsewhere
in the repo.include_str!("..."), include_bytes!("..."))import, require, use)Example output:
Untracked file check results:
src/data/schema.json— REFERENCED bysrc/parser.rsviainclude_str!("data/schema.json")— stage for commit?src/data/fixtures.json— in directory with modified files but no code references found — stage for commit?
Search the staged diff for secrets, credentials, or environment files that should not be committed:
git diff --cached | grep -iE '(password\s*=|API_KEY|SECRET_KEY|BEGIN.*PRIVATE KEY|.env)'
If any match is found, flag it to the user and do not proceed until the issue is resolved.
If the implementation changed public APIs, configuration options, or setup steps, verify that related documentation files (identified in Step 4) are still accurate. If a doc file describes behavior that was changed and was not already updated in Step 6, update it now. This check is lightweight — only flag docs that directly describe the modified behavior.
If the implementation wrote or modified documentation that includes examples combining narrative text with concrete data structures (mapping tables, code snippets, configuration blocks, example output), verify internal consistency:
Search for functions, methods, or logic in the repository that overlap with the code you wrote.
Use Grep or Serena's search_for_pattern to look for similar function names, string literals,
or algorithmic patterns. If you find that your new code substantially duplicates existing
utilities or helpers, refactor to reuse the existing code before proceeding.
If CONVENTIONS.md was loaded in Step 4 and verification commands were extracted,
run every CI check command recorded during the verification commands extraction.
Execute each command in sequence. If any command fails, fix the issue before
proceeding to the next command.
CONVENTIONS.md CI checks section (e.g., formatting, linting, type checking,
compilation). These commands are project-specific — do not hardcode or assume
which tools the project uses.If CONVENTIONS.md was not found or contained no CI check section, fall back to
running the project's standard build or lint step (if one exists) and compare the
warning output against the pre-implementation baseline captured during Step 7. If
new warnings were introduced, fix them before proceeding.
For each new feature or capability implemented, trace the data through its complete lifecycle to catch partial implementations:
Output the traced data flows and their completeness status to the user before proceeding.
Example output:
Data-flow trace results:
POST /api/v2/sbom→ parse request ✓ → validate ✓ → persist to DB ✓ → return response ✓ — COMPLETEparseLicense()→ extract license ID ✓ → resolve to SPDX ✓ → attach to package ✗ — INCOMPLETE (output not connected)
Verify that modified or created code fully honors its type contracts and maintains parity with sibling implementations.
find_symbol or Grep to locate the contract definition.Sibling parity (above) compares files in the same directory or module. When the implementation inserts into, updates, or deletes from a shared database entity (table, collection, or document store) that is used by multiple modules, extend the analysis across module boundaries to detect pattern inconsistencies.
insert(), update(), delete(), ORM save/create calls, raw SQL),
determine the target entity (table name, model class, or schema object).search_for_pattern (or Grep as fallback)
to find all other modules that interact with the same entity. Look for:
Example output:
Cross-module shared entity analysis results:
- Entity
source_document— 2 modules interact:
ingestor/graph/mod.rs: uses nested transaction withON CONFLICT DO UPDATEfor duplicate-key handlingrisk_assessment/service/mod.rs(new code): uses plaininsert()with no conflict handling — ANOMALY- Recommendation: adopt the ingestor's
ON CONFLICTpattern to handle duplicate inserts gracefully
When the implementation creates or modifies code that calls a shared abstraction (e.g., a mutation hook, API client method, service function, event emitter), verify that the call site follows the same patterns as existing callers of that abstraction.
Sibling parity (above) checks the definition of shared code; caller-site parity
checks how consumers invoke it. This catches anti-patterns introduced at the call
site — such as window.location.reload() in a mutation success callback when no
other caller in the codebase uses that pattern.
useDeleteSbomMutation,
an API client method like apiClient.post(), a service function like notifyUser()).find_referencing_symbols /
search_for_pattern to locate all other call sites of the same abstraction in the
codebase.onSuccess, .then(), or
after await — do callers use router navigation, cache invalidation, toast
notifications, or state updates?)onError, .catch() — do callers show error toasts,
log errors, or retry?)window.location.reload() when all other callers use
queryClient.invalidateQueries()), flag it as a potential anti-pattern. State
what the new code does, what existing callers do instead, and how many existing
callers were checked.Output the contract verification, sibling parity, cross-module shared entity, and caller-site parity results to the user before proceeding.
Example output:
Contract & sibling parity results:
StorageProviderimplementsProvidertrait —get()✓,list()✓,delete()✓,update()✗ — GAP (missingupdatemethod)- Sibling parity with
S3Provider,GcsProvider:
- Retry logic ✓ (all siblings use exponential backoff)
- Logging ✗ —
StorageProvidermissinginfo!()on successful operations — GAP- Config options ✓ (all accept
ProviderConfig)- Caller-site parity for
useDeleteSbomMutation:
- 3 existing callers found:
SbomList.tsx,SbomDetail.tsx,SbomActions.tsx- Success handling: all use
queryClient.invalidateQueries()+ toast notification- New code uses
window.location.reload()— ANOMALY (0 of 3 callers use this pattern)- Error handling ✓ (all callers including new code use
onErrortoast)
Commit following the Conventional Commits specification (https://www.conventionalcommits.org/en/v1.0.0/):
git commit --trailer="Assisted-by: Claude Code" -m "[optional scope]:
[optional body]
Implements "
Where type is one of: feat, fix, refactor, test, docs, chore, etc.
Use a scope when relevant (e.g. feat(api): add AIBOM endpoint).
The footer MUST reference the Jira issue ID.
Always include --trailer="Assisted-by: Claude Code" to attribute AI assistance.
Default flow (no Target PR):
Push the branch and open a pull request. In the PR description, use a Markdown link for the "Implements" line so the Jira issue is clickable:
where <webUrl> is the issue URL captured in Step 1 (e.g. Implements [PROJ-231](https://redhat.atlassian.net/browse/PROJ-231)).
If a GitHub issue reference was extracted in Step 1, append a Closes <owner>/<repo>#<number>
line to the PR description body. GitHub recognizes this keyword and will auto-close the
linked issue when the PR is merged. Do not add this to the commit message — only the
PR description.
Target PR flow:
When Target PR is present, push to the existing branch and update the PR description instead of creating a new PR:
git push
gh pr edit <pr-number> -R <owner/repo> --body "<updated-description>"
Add the current task's Jira issue ID to the PR description's Summary section
(e.g., a new bullet point describing the fix). Preserve the existing PR description
content — only append to the Summary bullets.Default flow (no Target PR):
Look up the Git Pull Request custom field ID from the project's Jira Configuration
section in CLAUDE.md (the field is listed as Git Pull Request custom field: <field-id>).
jira.update_issue(, fields={"": {"type": "doc", "version": 1, "content": [{"type": "paragraph", "content": [{"type": "inlineCard", "attrs": {"url": ""}}]}]}})
Add a comment to the Jira task:
jira.add_comment
Include:
Transition the task:
jira.transition_issue → In Review
Target PR flow:
When Target PR is present, use the PR URL from the Target PR section (not a newly created PR). Skip the custom field update — the PR link was already set when the original task's PR was created.
Add a comment to the Jira task:
jira.add_comment
Include:
Transition the task:
jira.transition_issue → In Review
get_symbols_overview, find_symbol, find_referencing_symbols to inspect code before modifying it. Check the Code Intelligence section for per-instance limitations. Fall back to Read/Grep/Glob for repos without a Serena instance.npx claudepluginhub mrizzi/sdlc-plugins --plugin sdlc-workflowFetches a JIRA issue and distills it into a structured task with acceptance criteria, sprint context, and codebase analysis. Surfaces missing criteria, scope, and risks, and can enrich the JIRA issue with analysis or spawn sub-tickets.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.