From engage-blueprint
Plan-first workflow: create a plan in Confluence from a Jira ticket, iterate on feedback, then implement.
How this skill is triggered — by the user, by Claude, or both
Slash command
/engage-blueprint:atlassian-blueprintThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill implements a three-phase workflow that bridges Jira tickets to code. Plans are reviewed in Confluence, workflow is managed via Jira ticket status, and code is delivered via pull requests.
This skill implements a three-phase workflow that bridges Jira tickets to code. Plans are reviewed in Confluence, workflow is managed via Jira ticket status, and code is delivered via pull requests.
Communication rules: The user may not be a developer. Never use git terminology (commit, push, stash, checkout, branch, merge, rebase, pull, PR) in messages to the user. Execute all git and CLI commands silently. Only surface results, progress, and errors in plain language. If a command fails, translate the error into a friendly message rather than showing raw output.
Before starting any phase, silently verify that Git is available. If any check fails, stop immediately with a clear, non-technical message.
Git — Run git --version
Git repository — Run git rev-parse --is-inside-work-tree
Only proceed if both checks pass.
Check .claude/plans/.confluence-config.json for azure.storageAccount and azure.container.
If both keys exist — verify access silently:
az storage container show --account-name <storageAccount> --name <container> --auth-mode login
If either key is missing — ask the user:
azure.storageAccountazure.containerThe config file now looks like:
{
"spaceKey": "TEAM",
"pages": { ... },
"azure": {
"storageAccount": "myaccount",
"container": "$web"
},
"snWebUxPath": "./en_ui"
}
When the Jira ticket is in an early-stage status (To Do, Open, Backlog, or similar).
getAccessibleAtlassianResources to get the cloud IDgetJiraIssue with the cloud ID and issue keyTell the user: "I've loaded the details for <JIRA_KEY>: . Let me explore the codebase and create a plan."
Before writing the plan, understand the codebase:
Spend adequate time here — the quality of the plan depends on understanding the code.
UI change identification is critical. While exploring, explicitly catalogue every user-visible UI change the ticket will require. This list drives the mandatory mockup step later — if a UI change is missed here, it won't get mocked.
E2E test discovery. If the ticket involves UI changes, also explore for E2E test planning:
playwright.config.ts exists somewhere — if not, the Playwright scaffold will need to be created as a prerequisite during implementation (see Phase 3, Step 5b).e2e/pages/ that can be reused.backend_helper.jsx → url_helper.jsx. These endpoints determine which fixtures are needed.data-testid attributes for stable test selectors.Create the plan file at .claude/plans/<JIRA_KEY>.md using this template. The template has two distinct halves: a business summary at the top for stakeholders and reviewers (written in plain, non-technical language), and a technical plan below the divider for developers.
# <JIRA_KEY>: <Jira Title>
## Summary
<2-3 sentence plain-language explanation of what this change does and why it's needed. No technical jargon. Write this for someone who uses the product but doesn't read code.>
## What Changes for Users
- <Visible change 1 — describe from the end-user's perspective>
- <Visible change 2>
- <If there are no user-visible changes, say "This is an under-the-hood improvement. Users won't see any difference, but it [improves performance / fixes a rare issue / etc.].">
## Decisions Needed
1. <Question or decision 1 that needs stakeholder input>
2. <Question or decision 2>
<If no decisions are needed, replace the numbered list with "No open questions — this one is straightforward.">
## UI Preview
<Include this section ONLY if the ticket involves user-visible UI changes. Delete it entirely for backend-only or non-visual work.>
[View all mockups](<azure-index-url>)
<List each mockup with its unique ID, deep link, and description:>
| ID | Preview | Description |
|----|---------|-------------|
| <JIRA_KEY>-M1 | [View](<mockup-1-url>) | <What this mockup shows> |
| <JIRA_KEY>-M2 | [View](<mockup-2-url>) | <What this mockup shows> |
## Risks & Impact
- <Plain-language risk 1, e.g. "Users will be logged out when we deploy this">
- <Plain-language risk 2, e.g. "This changes how notifications work, so we should let the support team know">
- <If low risk, say "Low risk — this change is isolated and doesn't affect other parts of the product.">
## Overall Feedback
> **Reviewers**: Use this section to leave any general comments about the plan — things that don't fit under a specific mockup or decision. For example: scope concerns, timeline thoughts, alternative approaches, or anything else you'd like to flag.
>
> *Highlight any text in this section and leave an inline comment with your overall feedback.*
---
*Everything below this line is technical detail for developers.*
---
## Jira Details
- **Type:** <issue type>
- **Priority:** <priority>
- **Description:** <description from Jira>
## Analysis
<Your analysis of the codebase and the problem. Reference specific files and code.>
## Approach
<High-level strategy for solving the problem. Explain WHY this approach was chosen.>
## Implementation Steps
1. <Step 1> — `path/to/file`
2. <Step 2> — `path/to/file`
...
## Files to Modify
- `path/to/file.ts` — <reason for change>
- `path/to/other.ts` — <reason for change>
## New Files
- `path/to/new-file.ts` — <purpose>
## Testing Strategy
- <How to verify the changes work>
- <Specific test cases to add or modify>
- **E2E Tests** (include this section if the ticket has UI changes):
- <User flow 1 to test — describe from the user's perspective>
- <User flow 2>
- **Fixtures needed:** <List API endpoints that need fixture JSON files in `en_ui/e2e/fixtures/`>
- **Page objects:** <List new page objects to create in `en_ui/e2e/pages/` or existing ones to extend>
- **`data-testid` attributes:** <List UI elements that need `data-testid` added during implementation>
## Technical Risks
- <Edge cases>
- <Potential breaking changes>
- <Dependencies or coordination needed>
Fill in every section thoroughly. The business summary sections should be understandable by anyone on the team. The technical sections should be detailed enough that a developer could implement the plan without additional context.
Enforcement rule — UI mockup gate: After writing the plan, review the "What Changes for Users" section. If ANY bullet point describes a visual or interface change (new buttons, layout changes, new screens, modified forms, style changes, etc.), then Step 4 (Build UI Mockups) is mandatory and every listed UI change must have a corresponding mockup. The plan MUST NOT be published to Confluence without mockups for all identified UI changes. There are no exceptions — if a mockup cannot be created, stop and ask the user for help rather than proceeding without it.
This step is mandatory if the plan identifies ANY user-visible UI changes. If the "What Changes for Users" section describes visual or interface changes, you MUST create mockups before proceeding. Do not skip this step. If you cannot create a mockup for a specific UI change (e.g., the relevant components don't exist in en_ui), stop and ask the user for guidance rather than skipping silently.
If the ticket has no UI-visible changes (backend-only, configuration, etc.), skip this step entirely.
If .claude/mockups/ does not already exist:
npm create vite@latest .claude/mockups -- --template react
cd .claude/mockups && npm install
cd .claude/mockups && npm install react-router-dom reactstrap bootstrap sass
.claude/mockups/ to the project's .gitignore if not already present.If .claude/mockups/ already exists, just ensure dependencies are installed:
cd .claude/mockups && npm install
The mockups MUST use the same CSS theme as the production app. There are two files to configure:
App.jsx — Theme import and routing:
import "bootstrap/dist/css/bootstrap.min.css" with the en_ui theme:
import "../../../en_ui/src/assets/scss/theme.scss";
(Path is relative from .claude/mockups/src/ → project root en_ui/.)HashRouter instead of BrowserRouter. Azure Blob Storage serves static files and does not support server-side routing, so SPA routes must use hash-based URLs (e.g., index.html#/CS-5864-M1).vite.config.js — Sass resolution:
The en_ui theme.scss uses @import "./node_modules/bootstrap/scss/..." which resolves relative to the file's own directory. Vite needs a custom importer to redirect these paths to en_ui/node_modules/. The config also sets up loadPaths and a ~ alias so all SCSS import styles used in en_ui work correctly:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
// en_ui is at the project root, two levels up from .claude/mockups/
const enUiPath = path.resolve(__dirname, '..', '..', 'en_ui')
export default defineConfig({
plugins: [react()],
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
silenceDeprecations: ['mixed-decls', 'color-functions', 'global-builtin', 'import'],
importers: [{
findFileUrl(url) {
// Redirect ./node_modules/... imports to en_ui/node_modules/...
if (url.startsWith('./node_modules/')) {
const resolved = path.join(enUiPath, url.slice(2))
return new URL('file:///' + resolved.replace(/\\/g, '/'))
}
return null
}
}],
loadPaths: [
enUiPath,
path.join(enUiPath, 'node_modules'),
path.join(enUiPath, 'src'),
path.join(enUiPath, 'src', 'assets', 'scss'),
],
},
},
},
resolve: {
alias: {
'~': path.join(enUiPath, 'node_modules'),
},
},
})
Both configurations are critical — without them, mockups will either fail to build or render without the correct branding.
Each mockup gets a unique ID using the pattern <JIRA_KEY>-M<n> where <n> is a sequential number starting at 1. For example: PROJ-123-M1, PROJ-123-M2, PROJ-123-M3. These IDs are used everywhere — file names, URLs, the plan, and the Confluence feedback sections — so reviewers can reference a specific mockup unambiguously.
Create the directory .claude/mockups/src/pages/<JIRA_KEY>/.
For each UI change identified in the plan, assign the next available mockup ID and create a React component file named <MOCKUP_ID>.jsx (e.g., PROJ-123-M1.jsx). Each component should:
en_ui (buttons, forms, layouts, etc.)Create an index page at .claude/mockups/src/pages/<JIRA_KEY>/index.jsx that:
Update .claude/mockups/src/App.jsx to add routes using HashRouter:
/ → index page for this ticket/<MOCKUP_ID> → individual mockup page (one route per mockup)The URL structure becomes index.html#/<MOCKUP_ID> which works on static hosting without server-side routing.
IMPORTANT — Base path: The --base flag must match the full URL path in Azure Blob Storage, which is /<container>/<JIRA_KEY>/ (e.g., /mockups/CS-5864/), NOT just /<JIRA_KEY>/. If the base path is wrong, assets (JS/CSS) will fail to load and the page will be blank.
IMPORTANT — Windows Git Bash: On Windows with Git Bash, paths starting with / get auto-converted to Windows paths (e.g., /mockups/ becomes C:/Program Files/Git/mockups/). Prefix the command with MSYS_NO_PATHCONV=1 to prevent this:
cd .claude/mockups && MSYS_NO_PATHCONV=1 npm run build -- --base=/<container>/<JIRA_KEY>/
On failure: "I ran into a problem building the UI previews. Here's what went wrong: [friendly one-sentence summary of the build error]." Then stop and ask the user how to proceed.
Read azure.storageAccount and azure.container from .claude/plans/.confluence-config.json, then upload:
az storage blob upload-batch \
--account-name <storageAccount> \
--destination <container>/<JIRA_KEY> \
--source .claude/mockups/dist \
--overwrite \
--auth-mode key
Note: Use --auth-mode key (not login). The login mode requires RBAC role assignments (Storage Blob Data Contributor) which may not be configured. The key mode uses the storage account key and works immediately.
On failure: "I couldn't upload the UI previews. This might be a permissions issue — please check with your team."
Since the mockups use HashRouter for client-side routing, all routes are served from index.html with hash fragments. Construct the URLs using the blob endpoint (not the static website endpoint):
https://<storageAccount>.blob.core.windows.net/<container>/<JIRA_KEY>/index.htmlhttps://<storageAccount>.blob.core.windows.net/<container>/<JIRA_KEY>/index.html#/<MOCKUP_ID>Do NOT use the static website endpoint (*.z13.web.core.windows.net) unless the container is $web. For named containers like mockups, use the blob endpoint directly.
Store the index URL and the list of mockup IDs with their descriptions and individual URLs. These will be used in the plan file, the Confluence page feedback sections, and the Jira comment.
Save the plan file to .claude/plans/<JIRA_KEY>.md. No git operations are needed at this stage — the plan is saved locally and will be published to Confluence in the next step.
Determine the Confluence space:
.claude/plans/.confluence-config.jsongetConfluenceSpaces to list available spaces.claude/plans/.confluence-config.json as {"spaceKey": "<KEY>"} for future runsFind or create the "Plans" folder page:
plansFolderId is already stored in .claude/plans/.confluence-config.jsonsearchConfluenceUsingCql with CQL: type = page AND space = "<spaceKey>" AND title = "Plans"plansFolderId in .claude/plans/.confluence-config.jsoncreateConfluencePage with title "Plans", an empty body (or a brief description like "This folder contains implementation plans for Jira tickets."), and no parentId. Store the new page's ID as plansFolderId in .claude/plans/.confluence-config.jsonCreate two Confluence pages (business summary + technical detail):
Important: All Atlassian MCP API calls during plan publishing must be made sequentially (one at a time). Do not make parallel calls to the Atlassian MCP — this can trigger rate limiting from Cloudflare.
Step 3a — Create the business summary page:
Call createConfluencePage with:
Space key from config
parentId set to the plansFolderId from config
Title: Plan: <JIRA_KEY> - <Jira Title>
Body: only the business sections of the plan — everything above the --- technical divider. This includes: Summary, What Changes for Users, Decisions Needed, UI Preview (if applicable), Risks & Impact.
If the ticket has UI mockups, append a per-mockup feedback section after the UI Preview table:
Mockup Feedback
Please leave feedback on each mockup below. Reference the mockup ID so we know exactly which one you mean.
<JIRA_KEY>-M1 —
Leave a comment on this section with your feedback for this mockup.
<JIRA_KEY>-M2 —
Leave a comment on this section with your feedback for this mockup.
Each mockup gets its own headed section so reviewers can leave inline Confluence comments directly on the relevant heading. This makes it clear which mockup the feedback applies to.
Omit the "Mockup Feedback" section entirely for non-UI tickets.
Always include an "Overall Feedback" section at the end of the business summary. This section is present on every plan — not just UI tickets — so reviewers have a clear place for general comments about scope, approach, timeline, or anything else:
Overall Feedback
Use this section to leave any general comments about the plan — things that don't fit under a specific mockup or decision. For example: scope concerns, timeline thoughts, alternative approaches, or anything else you'd like to flag.
Highlight any text in this section and leave an inline comment with your overall feedback.
At the very bottom of the business page, add a link to the technical detail page (use a placeholder initially; update it after the technical page is created):
Technical detail: Plan: <JIRA_KEY> - Technical Detail
Step 3b — Create the technical detail page:
createConfluencePage with:
Space key from config
parentId set to the business summary page ID (from Step 3a) — making it a child page
Title: Plan: <JIRA_KEY> - Technical Detail
Body: everything below the --- technical divider from the plan file. This includes: Jira Details, Analysis, Approach, Implementation Steps, Files to Modify, New Files, Testing Strategy, Technical Risks.
At the very top of the technical page, add a back-link to the business summary page:
Back to business summary: Plan: <JIRA_KEY> -
Step 3c — Update the business page link:
updateConfluencePage on the business summary page to replace the placeholder technical detail link with the real URL.Step 3d — Store both page IDs in .claude/plans/.confluence-config.json:
{
"pages": {
"<JIRA_KEY>": {
"pageId": "<business-summary-page-id>",
"technicalPageId": "<technical-detail-page-id>",
"syncState": "full",
"localPlanHash": "<SHA-256 of the local .md file>",
"lastSyncedAt": "<ISO 8601 timestamp>"
}
}
}
Create Jira remote link to the Confluence page (automatic bidirectional linking):
jira.email, jira.apiToken, jira.baseUrl from .claude/plans/.confluence-config.json~/.claude/projects/<sanitized-project-dir>/jira-credentials.json (where the sanitized dir replaces path separators with dashes)curl -s -X POST \
"<baseUrl>/rest/api/3/issue/<JIRA_KEY>/remotelink" \
-H "Authorization: Basic $(echo -n '<email>:<apiToken>' | base64 -w 0)" \
-H "Content-Type: application/json" \
-d '{
"object": {
"url": "<confluence-page-url>",
"title": "<confluence-page-title>",
"icon": {
"url16x16": "<baseUrl>/wiki/favicon.ico",
"title": "Confluence"
}
},
"relationship": "Confluence Page"
}'
Post the plan summary and questions on the Jira ticket:
Call addCommentToJiraIssue to post a comment that includes the business-facing sections from the plan. Use this format:
Plan ready for review
Summary:
What changes for users:
- <bullet points from the "What Changes for Users" section>
UI Preview: View mockups <Include this line ONLY if UI mockups were built in Step 4. Omit entirely for non-UI tickets.>
Risks & impact:
- <bullet points from the "Risks & Impact" section>
Decisions needed
- <question 1>
- <question 2>
Full plan details: Plan: <JIRA_KEY> -
This lets stakeholders review and respond directly in Jira without needing to visit Confluence. The Confluence link is still included for anyone who wants the full technical detail.
getTransitionsForJiraIssue to get available transitionstransitionJiraIssue to move the ticketTell the user in friendly language:
/engage-blueprint <JIRA_KEY> implement to start building"/engage-blueprint <JIRA_KEY> again and I'll pick up the comments."When the Jira ticket is in a review/planning status and implement was NOT passed.
.claude/plans/.confluence-config.json to get the page ID(s) for this Jira issue.
"CS-XXXX": "12345" — a single page ID containing the entire plan."CS-XXXX": { "pageId": "12345", ... } — a single page containing the entire plan."CS-XXXX": { "pageId": "12345", "technicalPageId": "12346", ... } — two pages: business summary + technical detail.pageId (the business summary page) and technicalPageId (the technical detail page, if present) from whichever format is found.getConfluenceSpaces then search for a page titled Plan: <JIRA_KEY>*/engage-blueprint <JIRA_KEY> on a ticket in 'To Do' status to start fresh."Gather feedback from Jira and Confluence (both footer and inline comments). For two-page plans, check both the business summary page and the technical detail page for comments.
Important: All Atlassian MCP API calls during feedback gathering must be made sequentially (one at a time). Do not make parallel calls to the Atlassian MCP — this can trigger rate limiting from Cloudflare.
getJiraIssue with the cloud ID and issue key, and read any comments on the ticket. Look for replies to the "Plan ready for review" comment that contain stakeholder answers to the decisions needed.getConfluencePageFooterComments with the business summary page ID to get reviewer comments left on the plan page. If technicalPageId exists, also call getConfluencePageFooterComments on the technical page.getConfluencePageInlineComments with the business summary page ID and resolutionStatus: "open" to get unresolved inline comments left directly on plan content (mockup headings, the "Overall Feedback" section, or any other text). If technicalPageId exists, also call getConfluencePageInlineComments on the technical page to get inline comments on the technical sections. For each inline comment, also call getConfluenceCommentChildren with commentType: "inline" to check for existing replies — if a reply contains "Addressed in plan update", treat the comment as already handled and skip it.getConfluencePage on the business summary page to read the current plan content. If technicalPageId exists, also call getConfluencePage on the technical page.Combine feedback from all sources. If the same question was answered in multiple places, prefer the most recent response.
Categorize feedback by section: When processing Confluence comments, identify which section each comment is attached to. For inline comments, use the textSelection property (the highlighted text) to determine which section the comment targets — match it against mockup headings (### <JIRA_KEY>-M1 — ...) or the "Overall Feedback" heading. For footer comments, infer the section from the comment content.
Mockup feedback — inline or footer comments on "Mockup Feedback" headings (e.g., ### PROJ-123-M1 — ...). Tag with the mockup ID for routing to the correct component during rebuild. Present grouped by ID:
Overall feedback — inline or footer comments on the "Overall Feedback" heading. These are general plan comments (scope, approach, timeline, alternatives). Present separately:
Other comments — comments on any other section. Present with the section name for context.
.claude/plans/<JIRA_KEY>.md (or fetch from Confluence if the local file is missing).claude/plans/<JIRA_KEY>.md with the changesIf the plan update in Step 3 affected any UI-related sections ("What Changes for Users", "UI Preview", or any mockup-relevant content), or if mockup-specific feedback was received:
.claude/mockups/src/pages/<JIRA_KEY>/, targeting each by its mockup ID (e.g., update PROJ-123-M1.jsx for feedback tagged to PROJ-123-M1). Leave unchanged mockups untouched.MSYS_NO_PATHCONV=1 on Windows Git Bash):
cd .claude/mockups && MSYS_NO_PATHCONV=1 npm run build -- --base=/<container>/<JIRA_KEY>/
az storage blob upload-batch \
--account-name <storageAccount> \
--destination <container>/<JIRA_KEY> \
--source .claude/mockups/dist \
--overwrite \
--auth-mode key
If no UI-related sections changed, skip this step.
Save the updated plan to .claude/plans/<JIRA_KEY>.md locally, then update Confluence. For two-page plans, split the updated content and update each page separately. Because inline comments are anchored to specific text via textSelection, updating the page content can cause inline comments to become dangling (losing their anchor) if the text they were attached to changed. To prevent feedback loss, follow this sequence:
Important: All Atlassian MCP API calls during plan updates must be made sequentially (one at a time). Do not make parallel calls to the Atlassian MCP — this can trigger rate limiting from Cloudflare.
Before calling updateConfluencePage, read all open inline comments from both the business summary page and the technical detail page (if it exists) using getConfluencePageInlineComments with resolutionStatus: "open". For each comment, record:
textSelection (the highlighted text it was anchored to)For any unaddressed inline comments (feedback that was not incorporated in this update — e.g., deferred decisions, questions still pending), embed them directly into the relevant section of the updated plan content as attributed blockquotes. This ensures they survive the page update even if the original anchor text changes.
Format each embedded comment as:
Reviewer feedback (, ): ""
Place each embedded comment in the section it was originally attached to:
### <JIRA_KEY>-M<n> heading--- dividerIf the target section was removed or heavily rewritten, place the comment in an "Unresolved Feedback" section appended just above the technical divider:
Unresolved Feedback
The following reviewer comments could not be anchored to their original sections after this update:
- "" — (originally on: )
Split the updated plan content at the --- technical divider:
Update the business summary page — Call updateConfluencePage with the business page ID and the business sections (everything above the --- divider), including the link to the technical detail page at the bottom.
Update the technical detail page — If technicalPageId exists, call updateConfluencePage with the technical page ID and the technical sections (everything below the --- divider), including the back-link to the business page at the top.
Migrate legacy single-page plans — If the config entry has no technicalPageId (legacy single-page format), create a new technical detail child page using the same approach as Phase 1 Step 3b, then update the config to add the technicalPageId.
Update sync state — After both pages are updated, compute the SHA-256 hash of the local .md file and update localPlanHash and lastSyncedAt in .confluence-config.json.
After updating the pages, reply to each addressed inline and footer comment (on whichever page it was found):
createConfluenceInlineComment with parentCommentId set to the original comment's ID. Use this format: "Addressed in plan update — [one-sentence summary of the change made]." For example: "Addressed in plan update — made the submit button larger and changed colour to primary blue."createConfluenceFooterComment with parentCommentId to acknowledge feedback was addressed.This creates an audit trail: reviewers can see their original comment plus the reply confirming it was handled.
Tell the user:
/engage-blueprint <JIRA_KEY> implement to start building."When the Jira ticket is in an approved status and implement was explicitly passed. This phase writes real code.
getJiraIssue and check its current statusThis is a hard gate. Under no circumstances should implementation proceed without the ticket being in an approved status.
Escape valve: If the user says they are the only person on the project and cannot get someone else to approve, tell them: "You can move the ticket to the approved status yourself in Jira, and then run this command again."
Before building, check whether any comments were left after the plan was last updated. For two-page plans, check both pages.
Important: All Atlassian MCP API calls during feedback checking must be made sequentially (one at a time). Do not make parallel calls to the Atlassian MCP.
getConfluencePage — note the page's version.when (last modified timestamp). If technicalPageId exists, also read the technical detail page.getConfluencePageFooterComments on the business summary page. If technicalPageId exists, also check the technical page.getConfluencePageInlineComments with resolutionStatus: "open" on the business summary page. If technicalPageId exists, also check the technical page. For each open inline comment, call getConfluenceCommentChildren with commentType: "inline" to check for replies. An inline comment is addressed if it has a reply containing "Addressed in plan update"; otherwise it is unaddressed.getJiraIssue.If unaddressed comments are found (from any source):
If no unaddressed comments are found, proceed to Step 3.
Tell the user: "The plan is approved! I'm setting things up to start building."
Check for unsaved work (silently):
git status --porcelaingit stash push -m "engage-blueprint: saved before <JIRA_KEY>"Create the implementation branch (silently):
fix-login-timeout-on-mobilegit fetch origin
git checkout main
git pull origin main
git checkout -b <JIRA_KEY>-<slugified-title>
git push -u origin <implementation-branch>
git fetch origin
git checkout <implementation-branch>
git pull origin <implementation-branch>
Error translation guide — if any git command fails, do NOT show the raw error. Translate it:
- "fatal: A branch named 'X' already exists" → "It looks like work on this ticket was already started. Let me pick up where we left off."
- "error: failed to push" → "I wasn't able to upload the working area. This might be a permissions issue — please check with your team."
- Any other error → "Something went wrong while setting up. Here's a summary: [one-sentence plain description]. You may want to ask a developer for help."
Read .claude/plans/<JIRA_KEY>.md and parse the implementation steps.
If the plan file doesn't exist locally, fetch it from Confluence:
getConfluencePage using the page ID from .claude/plans/.confluence-config.json.claude/plans/<JIRA_KEY>.mdPresent the plan summary to the user in plain language and ask for confirmation: "Here's what I'm going to build: [summary]. Ready to go?"
Work through each implementation step from the plan:
For each step:
Follow the testing strategy from the plan:
Save work logically — group related changes into coherent saves rather than one giant save. Execute git commands silently:
git add <relevant-files>
git commit -m "<descriptive message>"
If the plan's Testing Strategy includes an E2E Tests section, generate Playwright end-to-end tests for the UI changes. Skip this step entirely if there are no E2E tests in the plan.
Tell the user: "I'm now setting up automated browser tests for the changes."
If en_ui/e2e/playwright.config.ts does not exist, the Playwright scaffold needs to be created first:
cd en_ui && npm install && npx playwright install chromium
Then create the scaffold structure. The following files must exist (create from the established templates in en_ui/e2e/):
e2e/playwright.config.ts — Two projects: mock (CI, uses USE_MOCK_API=true) and live (post-deployment, hits real backend)e2e/utils/api-mock.ts — setupApiMocks(page, routeMap) utility that registers page.route() handlers from JSON fixture files. In mock mode, unmatched API calls return HTTP 500 with a descriptive error. In live mode, this is a no-op.e2e/utils/auth.ts — setupAuth(page) utility that injects localStorage values (authUser, selectedSchoolId, userRole, InternalUserId, actingAsRole) to bypass login for non-login tests. Also exports loginViaApi(page) for live mode.e2e/fixtures/auth/login-success.json, e2e/fixtures/auth/user-profile.json, e2e/fixtures/common/menu-options.json — Seed fixtures for auth flows.e2e/pages/login.page.ts — Login page object.e2e/tests/auth/login.spec.ts — Seed login tests.If the scaffold already exists, just ensure Playwright is installed: cd en_ui && npx playwright install chromium.
data-testid attributesDuring Step 5 (when implementing the feature code), data-testid attributes should have been added to the elements listed in the plan's E2E test section. Verify they are in place. If any are missing, add them now.
Selector priority for page objects: data-testid > role > text/placeholder > never CSS classes (Bootstrap classes change).
For each API endpoint listed in the plan's "Fixtures needed" section:
e2e/fixtures/yield call(backendHelperFn) → backend_helper.jsx function → axios response shapee2e/fixtures/<domain>/<endpoint-name>.jsonEach fixture file contains exactly what the API returns (the response body). Keep fixture data minimal but realistic.
For each page or component under test:
e2e/pages/<PageName>PagePage instance and sets up locatorsdata-testid → getByRole → getByPlaceholder/getByText → never CSS classesgoto()) and action methods (fillForm(), submit())Create test files in en_ui/e2e/tests/<domain>/:
setupApiMocks from ../../utils/api-mocksetupAuth from ../../utils/auth for tests that need pre-authenticated statesetupApiMocks(page, routeMap) with the fixture map for that test's API dependenciessetupAuth(page) before navigatingtest.describe and test() names that read like user storiescd en_ui && USE_MOCK_API=true npx playwright test --config=e2e/playwright.config.ts --project=mock
All tests must pass before proceeding. If tests fail, fix the issues (missing fixtures, wrong selectors, timing issues) and re-run.
Save the E2E test files as a separate commit:
git add en_ui/e2e/
git commit -m "Add E2E tests for <JIRA_KEY>"
After implementation is complete:
Run the full test suite if one exists, including E2E tests if they were generated in Step 5b. Tell the user: "I'm running tests to make sure everything works."
cd en_ui && USE_MOCK_API=true npx playwright test --config=e2e/playwright.config.ts --project=mockShow the user a plain-language summary of what was built, organized by what changed from the user's perspective (not file-by-file).
Ask: "Would you like me to submit this for code review?"
If yes, first verify the GitHub CLI is available:
gh --version. On failure: "The GitHub command-line tool isn't installed. I've finished building everything, but I can't submit it for review automatically. Please ask your team's developer to install it from https://cli.github.com/ and then run this command again."gh auth status. On failure: "The GitHub tool is installed but not signed in. Please ask your team's developer to run gh auth login in a terminal, and then run this command again."Create the PR:
gh pr create \
--base <starting-branch> \
--head <implementation-branch> \
--title "<JIRA_KEY>: <Jira Title>" \
--body "$(cat <<'EOF'
## Summary
<brief summary of changes>
## Jira
[<JIRA_KEY>](<jira-url>)
## Plan
See the [implementation plan](<confluence-page-url>) for full details.
## Changes
<list of key changes>
## Test plan
<how the changes were tested>
---
Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
Tell the user: "Done! Your code has been submitted for review: [PR URL]"
Update the Jira ticket:
addCommentToJiraIssue to post: "Implementation complete. Code review: [PR URL]"transitionJiraIssue to move the ticket to an "In Code Review" or "In Progress" status if availableIf the user's work was saved aside at the start (via git stash), restore it now:
git stash list | grep "engage-blueprint: saved before <JIRA_KEY>"
If found, run git stash pop and tell the user: "I've restored the work you had in progress before we started."
| Branch | Pattern | Example |
|---|---|---|
| Implementation | <JIRA_KEY>-<slugified-title> | PROJ-123-fix-login-timeout |
a-z, 0-9, or - with a hyphenThe file .claude/plans/.confluence-config.json stores persistent configuration:
{
"spaceKey": "TEAM",
"plansFolderId": "plans-parent-page-id",
"pages": {
"PROJ-123": "confluence-page-id-here",
"PROJ-456": {
"pageId": "business-summary-page-id",
"technicalPageId": "technical-detail-page-id",
"syncState": "full",
"localPlanHash": "<SHA-256 of the local .md file>",
"lastSyncedAt": "<ISO 8601 timestamp>"
}
},
"azure": {
"storageAccount": "myaccount",
"container": "$web"
},
"snWebUxPath": "./en_ui",
"jira": {
"email": "[email protected]",
"apiToken": "ATATT3x...",
"baseUrl": "https://yoursite.atlassian.net"
}
}
Page entry formats (all three must be supported for backward compatibility):
"PROJ-123": "12345" — single page ID"PROJ-123": { "pageId": "12345", "syncState": "full", ... } — single page with sync trackingTo extract the business page ID from any format, use: if the value is a string, use it directly as the page ID; if it's an object, use the pageId field.
Security note: The
jirablock stores a shared team API token in the repo. This is intentional for team-wide automation. Treat it as a shared credential — rotate it if team membership changes.
This file is created automatically on first run when the user selects a Confluence space. Azure storage and en_ui settings are added when first needed for UI mockups.
The Atlassian MCP proxy routes through Cloudflare, which enforces a Web Application Firewall (WAF). Large page bodies — especially those containing security-related technical terminology — can trigger a Cloudflare block. The two-page architecture (business summary + technical detail as a child page) is the primary defence against this, since each page body is significantly smaller than a combined single-page upload.
All Atlassian MCP API calls must be made sequentially — one at a time, never in parallel. Concurrent requests to the Atlassian MCP can trigger Cloudflare rate limiting, which is a separate issue from WAF content blocking. This applies to all phases: creating pages, reading comments, updating content, and posting replies.
When publishing or updating Confluence pages, use this approach:
Always use the two-page architecture — Business summary content goes to the main page; technical detail goes to the child page. This keeps each upload well under the WAF size threshold.
On Cloudflare block (HTTP block / "Attention Required" response) for the technical detail page — Do not retry the same payload immediately. Instead:
Plan: <JIRA_KEY> - Technical Detail (Part 1) — Analysis, Approach, Implementation StepsPlan: <JIRA_KEY> - Technical Detail (Part 2) — Files to Modify, New Files, Testing Strategy, Technical RiskssyncState: "partial" and tell the user: "Most of the plan uploaded successfully, but the full technical detail was too large for Confluence. The complete plan is in the local file at .claude/plans/<JIRA_KEY>.md."Record the sync state — After any upload, record the outcome in .claude/plans/.confluence-config.json:
{
"pages": {
"PROJ-123": {
"pageId": "12345",
"technicalPageId": "12346",
"syncState": "full",
"localPlanHash": "<SHA-256 of the local .md file contents>",
"lastSyncedAt": "<ISO 8601 timestamp>"
}
}
}
syncState: "full" — Both Confluence pages have the complete plan.syncState: "partial" — Business summary uploaded successfully; technical detail may be incomplete. The local file is the source of truth for technical detail.At the start of Phase 2 (Review & Adjust) and Phase 3 (Implement), before doing any other work, check whether the Confluence pages are in sync with the local plan:
Read the local plan from .claude/plans/<JIRA_KEY>.md and compute its SHA-256 hash.
Compare against localPlanHash in the config:
syncState is "full" → Confluence is up to date. Proceed normally.--- divider). Update localPlanHash and lastSyncedAt on success.syncState is "partial" → Confluence has incomplete technical detail from a previous failed upload. Attempt a re-upload of the technical detail page. If it succeeds, update syncState to "full". If it fails again, proceed with the local plan as source of truth.If the local file is missing but a Confluence page exists:
--- divider + technical sections) and save it to .claude/plans/<JIRA_KEY>.md.syncState was "partial", warn the user: "I recovered the plan from Confluence, but the technical detail may be incomplete. Please check the plan before proceeding."During Phase 3 (Implement), always read the plan from the local file (.claude/plans/<JIRA_KEY>.md), never from Confluence. The local file is guaranteed to have the full technical detail regardless of syncState. Only fall back to Confluence if the local file is missing.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub glaidler/claude_skills --plugin atlassian-blueprint