Mapping OpenSpec to Jira: SDD Without Abandoning Your Backlog
Specs and change proposals live in the repo; Jira still owns Epics, Stories, and the board. You can do both without maintaining two truths: tie one OpenSpec change folder to one Story, leave
tasks.mdout of Sub-tasks, and link every ticket toopenspec/changes/.... Most organizations will not retire Jira; this split is how you get SDD anyway without duplicate work or broken traceability.
Contents
- 1. Context & The Core Tension
- 2. Anatomy of What You’re Mapping
- 3. Four Mapping Strategies
- 4. How to Break a Spec Down
- 5. Is Breaking Specs into Jira Tickets Still Good Practice?
- 6. Recommended Workflow (Strategy D in practice)
- 7. Anti-Patterns
- 8. Tooling for Sync & Automation
- 9. Worked Example
- 10. Decision Matrix
- 11. Summary
1. Context & The Core Tension
OpenSpec is a lightweight spec-driven development (SDD) framework where specifications live in the repository alongside code. Each active change is a folder; it normally contains a proposal, a delta to the spec, a tasks.md checklist, and often design.md for nontrivial work:
openspec/
├── specs/ # Source of truth (current system behavior)
│ └── <domain>/
│ └── spec.md
└── changes/
└── <change-name>/
├── proposal.md # Why & what is changing
├── design.md # Technical approach (optional)
├── tasks.md # Implementation checklist
└── specs/
└── <domain>/
└── spec.md # Delta (ADDED / MODIFIED / REMOVED)
Jira, by contrast, uses a flat three-level hierarchy:
Epic
└── Story | Task | Bug # Peers at the same level
└── Sub-task
A widely-held misconception is that Jira supports Epic → Story → Task → Sub-task. It does not. Stories and Tasks are siblings, not parent-child. Any deeper hierarchy (Initiative above Epic, Feature between Epic and Story) requires Jira Premium with Advanced Roadmaps or a marketplace app like Structure or JXL.
The tension: OpenSpec’s natural unit of work is the change folder (a proposal plus its specs, design, and tasks). Jira’s natural unit of work is the issue (Epic, Story, Task). They do not line up 1:1; forcing a match yields double-bookkeeping or a lossy map.
Below are four ways to line them up, when each tends to work, and a workflow that usually survives contact with a real backlog.
2. Anatomy of What You’re Mapping
2.1 OpenSpec artifacts in detail
| Artifact | Content | Granularity |
|---|---|---|
specs/<domain>/spec.md | Living requirements for a capability. Uses SHALL/MUST statements with Given/When/Then scenarios. | Domain-level, persistent |
changes/<name>/proposal.md | Why this change exists, what’s in scope, rollback plan | One change = one concrete feature/modification |
changes/<name>/design.md | Technical decisions, data model changes, risks, sequence diagrams | One change, optional for small work |
changes/<name>/tasks.md | Checkboxed implementation list: - [ ] 1.1 Add theme context provider | Dozens of items per change |
changes/<name>/specs/<domain>/spec.md | Delta: what’s being ADDED / MODIFIED / REMOVED in the living spec | Per-requirement diffs |
A delta spec nests scenarios under requirements inside one file. That is roughly the shape of Epic → Story → acceptance criteria, except it stays plain text in git instead of a ticket tree.
2.2 Jira artifacts in detail
| Issue Type | Purpose | Typical duration |
|---|---|---|
| Epic | Large goal-driven body of work, usually cross-sprint | Weeks to months |
| Story | User-facing feature/requirement with clear value | Fits in one sprint |
| Task | Non-user-facing work (infra, refactor, ops) | Fits in one sprint |
| Sub-task | Concrete implementation step under a Story or Task | Hours to a day or two |
| Bug | Defect | Variable |
Stories and Tasks are siblings. Sub-tasks are strictly within the same project as their parent. Epics can span projects.
3. Four Mapping Strategies
Strategy A — Change = Epic (1:1 mapping)
The most direct mapping. Each OpenSpec change folder corresponds to one Jira Epic.
OpenSpec change folder Jira
───────────────────────── ───────────────────
changes/add-dark-mode/ ──► Epic: "Add dark mode"
proposal.md ──► (Epic description)
specs/ui/spec.md ──►
Requirement 1 ──► Story 1
Scenario A ──► (acceptance criterion)
Scenario B ──► (acceptance criterion)
Requirement 2 ──► Story 2
tasks.md ──►
1.1, 1.2, 1.3 ──► Sub-tasks under Story 1
2.1, 2.2 ──► Sub-tasks under Story 2
design.md ──► Attached/linked to Epic
When it fits: Small-to-mid teams with one Jira project per system, where each change is a genuine Epic-sized effort (multi-sprint, multiple requirements).
Problems:
- Small OpenSpec changes (a single requirement modification) become over-specified Epics with one Story.
- Jira Sub-tasks are scoped to the same project as the parent — if
tasks.mditems cross repos or teams, you cannot model them as Sub-tasks cleanly. - You maintain requirements in two places: delta spec and Story descriptions. That is the double-bookkeeping trap.
Strategy B — Domain = Epic, Change = Story
Shift everything up one level. The Epic represents the long-lived domain or capability (matching openspec/specs/<domain>/), and each change folder maps to a Story under that Epic.
openspec/specs/auth/spec.md ──► Epic: "Authentication"
changes/add-2fa/ ──► Story: "Add 2FA"
tasks.md items ──► Sub-tasks
changes/fix-session-expiry/ ──► Story: "Fix session expiry"
tasks.md items ──► Sub-tasks
When it fits: Product-led teams where “Authentication,” “Payments,” “Notifications” are long-running investment areas with a continuous stream of small changes.
Problems:
- Epics never close — they’re effectively themes or components. This breaks Jira’s Epic Burndown report, which assumes Epics have a lifecycle.
- Cross-domain changes (a change that touches
auth/andpayments/) need to live under two Epics, which Jira does not support cleanly for a single Story.
Mitigation: Use Jira Components or Labels for the domain, and let Epics remain genuine initiatives. This effectively moves you toward Strategy D.
Strategy C — Initiative → Epic = change (Jira Premium)
Jira Premium and Advanced Roadmaps add a level above Epic, usually Initiative (portfolio or program: a quarter, theme, or release train). Map one OpenSpec change to one Epic under that Initiative; under the Epic, use Stories for big requirements and Sub-tasks for tasks.md steps only if you want a strict ticket mirror. That layer avoids some “Epic” naming clashes from Strategy A, at the cost of license and admin overhead.
Initiative: "Q2 Security Improvements"
├── Epic: Change "add-2fa" ──► openspec/changes/add-2fa/
│ ├── Story: Requirement 1 ──► delta spec requirement 1
│ └── Story: Requirement 2 ──► delta spec requirement 2
│ └── Sub-task: task 1.1 ──► tasks.md item 1.1
└── Epic: Change "enforce-pw-rotation"
When it fits: Enterprises on Jira Premium that need SAFe-style planning, portfolio visibility, and regulatory traceability.
Problems:
- Cost and complexity. Advanced Roadmaps is significant overhead.
- You still need discipline to keep spec deltas and Story descriptions in sync.
Strategy D — What/How separation (recommended for most teams)
Do not mirror the whole tree. Jira is for planning and status; the repo is for the spec and the checklist you actually execute against.
| Jira (coordination) | OpenSpec (spec and execution) |
|---|---|
| Epic — business outcome or program slice | Groups several changes; not a single file in the repo |
| Story or Task — one deliverable, one owner, one card | One changes/<name>/ folder per ticket, linked from the description. That folder holds proposal.md, optional design.md, tasks.md, and the delta under specs/.../spec.md |
| Acceptance criteria — short bullets for stakeholders | The delta spec’s SHALL / MUST and Given / When / Then. If Jira and the spec disagree, trust the spec. |
| Status, priority, sprint, assignee, estimates | Behavior and the ordered list in tasks.md (do not retype into Sub-tasks) |
Mapping rule: A Jira Story/Task is linked to exactly one OpenSpec change folder (via a link in the Story description: Spec: openspec/changes/add-2fa/). The Jira Epic groups related Stories into a business initiative.
Do not mirror tasks.md into Jira Sub-tasks. That checklist is for the agent and for whoever is implementing; copying it into Jira adds board noise, weakens the checklist inside the agent context, and means updating two systems for the same steps.
Jira Epic: "Ship 2FA for all users" ──► business initiative
├── Story: "User enables 2FA at signup" ──► links to openspec/changes/add-2fa/
│ Acceptance criteria (brief, user-visible) ──► (detailed scenarios live in spec.md)
├── Story: "Admin enforces 2FA for org" ──► links to openspec/changes/enforce-2fa-policy/
└── Task: "Add TOTP secret storage column" ──► links to same change, infra work
When it fits: Most teams. Treat this as the default unless you have a specific reason for A, B, or C.
Why it works: Product can live entirely in Jira. Engineering keeps a single normative spec next to the code. The Story carries intent and delivery tracking; the delta spec carries behavior. Stakeholders usually only need status at Story granularity anyway.
4. How to Break a Spec Down
Whichever strategy you use, decompose work the same way. OpenSpec’s flow is propose → apply (work through tasks.md) → archive, merging the delta into living specs/. Section 6 pairs that lifecycle with Jira transitions; Section 3 only fixes which issue type holds the link to the change folder.
Step 1 — Start with the capability (domain)
A capability is a noun in your system: Authentication, Checkout, Notifications, Reporting. Each one gets a specs/<domain>/spec.md file. In Jira, this is typically a Component, Label, or (under Strategy B) an Epic.
Step 2 — Scope the change
A change is one coherent modification to one or more capabilities. Good test: “Can I describe the change in one sentence that starts with a verb?” — “Add 2FA,” “Deprecate remember-me cookie,” “Migrate sessions to Redis.” If you need “and,” split it.
One change = one changes/<name>/ folder = (under Strategy D) one Jira Story, or (under Strategy A) one Jira Epic.
Step 3 — Enumerate requirements in the delta spec
Each requirement is a SHALL/MUST statement: user-visible behavior lives here. Map requirements to Story acceptance criteria (bullets under one Story) unless the item needs its own planning, status, and owner — then promote it to a Story.
Rule of thumb: if it could ship, test, and demo on its own, give it a Story. If not, keep it as a criterion on the parent Story.
Step 4 — Generate tasks.md
Tasks are technical, granular, and ordered. They are the AI agent’s to-do list. They do NOT map to Jira. They belong in the repo only.
Good task: - [ ] 1.1 Add totp_secret column to users table with migration Bad task: - [ ] Implement 2FA (too vague — this is a Story, not a task) Bad task: - [ ] Add a comma (too granular — this is editing, not a task)
Step 5 — Size check
If a change has more than ~15 tasks or more than ~5 requirements, it’s probably too big. Split it into multiple changes. This keeps the AI agent’s context window tight and the Jira Story sprint-sized.
5. Is Breaking Specs into Jira Tickets Still Good Practice?
Yes — if tickets track delivery, not a second copy of the spec, and granularity stays sane.
Arguments for continuing to use Jira alongside OpenSpec
- Non-dev stakeholders. Product, design, QA, support, and leadership already live in Jira. Git is a barrier for many of them. The Story is the contract they read.
- Portfolio visibility. Roadmaps, burndown, sprint planning, capacity. Jira does that; OpenSpec does not try to.
- Cross-team dependencies.
Blocks,Is blocked by,Relates tolink types in Jira are how you coordinate work across teams. OpenSpec has no equivalent. - Compliance and audit. Regulated industries need an auditable trail of “who approved what, when” at the ticket level. Git history alone is often not enough for auditors who expect ticket-level approvals.
- Existing org muscle memory. If your org measures delivery in Jira, fighting that is expensive and usually loses.
Arguments against (or at least cautions)
- Double-bookkeeping risk. If you mirror every delta requirement as a Story and every task as a Sub-task, you now maintain requirements in two systems. Either one drifts from the other, or developers waste time syncing.
- Jira-as-spec. Story descriptions are not where behavior should live long term. OpenSpec only helps if
spec.mdnext to the code stays authoritative. - Granularity mismatch.
tasks.mditems are often too small for Jira Sub-tasks and too numerous for sprint boards. Forcing them in creates board noise. - Sprint ceremony overhead. SDD works best when the spec/apply/archive loop is tight. Heavy Jira ceremony (pointing, grooming, status meetings for every Sub-task) slows that loop down.
The verdict
Keep Jira as delivery and coordination, not as the spec. Keep normative behavior in the repo and link tickets to it — Strategy D.
6. Recommended Workflow (Strategy D in practice)
This workflow assumes an AI assistant can read the repo and Jira (for example via MCP).
6.1 Starting from a Jira Story
- PO creates Epic & Stories in Jira. Business intent, acceptance criteria in user-visible language, priority, sprint assignment.
- Developer picks up a Story. Moves it to In Progress (or a custom “Ready for Spec” state).
- Developer runs
/opsx:propose "<story summary>"with the Jira ticket ID in the prompt. The AI agent reads the Jira ticket via MCP and generates:changes/<change-name>/proposal.md— includes the Jira ticket ID in the headerchanges/<change-name>/specs/<domain>/spec.md— delta with requirements & scenarios translated from the Story’s acceptance criteriachanges/<change-name>/design.md— technical decisionschanges/<change-name>/tasks.md— implementation checklist
- Developer reviews the proposal. Stop here if the spec is wrong; fixing prose after code exists is expensive.
- Developer runs
/opsx:apply. The AI works throughtasks.mdcheckbox by checkbox. Optional: automation updates the Jira Story status on first task completion. - Developer runs
/opsx:archive. The delta merges intoopenspec/specs/<domain>/spec.md. The Jira Story transitions to Done. A PR is opened linking back to both.
6.2 Jira field conventions
Add two custom fields (or use existing ones consistently):
- Spec Path — text field containing the path to the OpenSpec change folder, e.g.
openspec/changes/add-2fa/. - Spec Status — automation-driven field mirroring OpenSpec state (
proposed/in-progress/applied/archived).
In Story descriptions, always include:
Spec: openspec/changes/add-2fa/
Delta: openspec/changes/add-2fa/specs/auth/spec.md
6.3 Status mapping
| OpenSpec phase | Jira transition |
|---|---|
| Change folder created, proposal drafted | Story → In Progress (or Spec in Review) |
| Proposal approved, apply started | Story → In Progress / Implementation |
| All tasks complete, PR opened | Story → In Review |
| Archived, merged into main spec | Story → Done |
6.4 What does NOT go into Jira
- Individual
tasks.mdcheckboxes - Design decisions from
design.md(link the file, don’t paste it) - Scenario steps (
GIVEN/WHEN/THEN) — summarize as acceptance criteria, keep the detail in the spec
6.5 What must always be in Jira
- The business-facing Epic
- The Story with concise acceptance criteria
- A link to the OpenSpec change folder
- Status, priority, assignee, sprint
7. Anti-Patterns
- One Jira Sub-task per
tasks.mdcheckbox. Explodes board noise, double-bookkeeping, no added value. - Jira Story description as source of truth for behavior. Defeats SDD. The spec file must be authoritative.
- An Epic for every small change. Breaks Epic Burndown. Use Strategy D’s Story mapping instead.
- No link between Jira and the change folder. Auditors and future developers can’t reconstruct why decisions were made.
- Requirements written only in Jira, never in
spec.md. The AI agent has no durable context and will drift. - Deleting the change folder after archive. OpenSpec moves it to
changes/archive/for a reason — that’s your audit trail. - Over-abstracting with Initiatives. Only valuable if you have genuine portfolio-level investment streams. Otherwise adds ceremony.
8. Tooling for Sync & Automation
Several tools reduce manual sync between OpenSpec and backlog tools:
- Linear MCP + OpenSpec MCP — Well-trodden combo: agent reads the ticket, writes the change folder, can bump status. Jira can follow the same shape through Atlassian’s MCP server if your org uses that instead of Linear.
- SpecFact CLI — Bidirectional sync between OpenSpec and GitHub Issues or Azure DevOps. Status mapping is built in. Jira support has been discussed in their roadmap.
- Atlassian MCP + custom prompts — With Jira MCP connected, a developer can ask the agent: “Read JIRA-1234, draft the OpenSpec change, and update the ticket status when done.” No custom tooling beyond good prompting.
- Jira Automation rules — Rule: When a comment on a Story matches
openspec-status: applied, transition the Story to In Review. Lets the OpenSpec workflow write back without needing code changes on the Jira side.
Reach for the smallest integration that works. Wiring MCP plus two agreed fields often beats buying a sync product you still have to babysit.
9. Worked Example
9.1 The Jira Epic (PO-authored)
Epic JIRA-500: Ship 2FA for all users
Goal: Reduce account takeover incidents by 80%
Target: Q2
Stories:
JIRA-501 — User enables TOTP at signup
JIRA-502 — User manages 2FA in account settings
JIRA-503 — Admin enforces 2FA org-wide
9.2 The Jira Story (PO-authored)
JIRA-501 — User enables TOTP at signup
As a new user
I want to set up an authenticator app during signup
So that my account is protected from day one
Acceptance criteria:
- TOTP setup is offered during signup flow
- User can scan QR code with standard authenticator apps
- User can skip setup but is prompted again on next login
- Recovery codes are generated and shown once
Spec: openspec/changes/add-totp-at-signup/
9.3 The OpenSpec change (dev-authored via AI)
openspec/changes/add-totp-at-signup/
├── proposal.md
│ "Implements JIRA-501. Adds TOTP enrollment to the signup flow.
│ In scope: enrollment UI, secret storage, QR generation, recovery codes.
│ Out of scope: SMS fallback, WebAuthn."
├── design.md
│ "Secrets stored encrypted at rest via KMS. QR generated client-side.
│ Recovery codes are 10 single-use codes, hashed server-side."
├── specs/auth/spec.md (delta)
│ ## ADDED Requirements
│ ### Requirement: TOTP Enrollment at Signup
│ The system SHALL offer TOTP enrollment during signup.
│ #### Scenario: User scans QR code
│ - GIVEN a new user completing signup
│ - WHEN they choose "Set up 2FA"
│ - THEN a QR code encoding a TOTP secret is displayed
│ - AND a six-digit code entered from an authenticator verifies the secret
│ #### Scenario: User skips enrollment
│ - GIVEN a new user completing signup
│ - WHEN they choose "Skip for now"
│ - THEN the account is created without 2FA
│ - AND a reminder is shown on their next login
└── tasks.md
- [ ] 1.1 Add totp_secret (encrypted) and recovery_codes columns
- [ ] 1.2 Implement TOTP secret generator using otpauth library
- [ ] 1.3 Add /signup/2fa route and QR rendering
- [ ] 1.4 Verify entered code against secret
- [ ] 1.5 Generate & hash 10 recovery codes
- [ ] 1.6 Display recovery codes modal (one-time)
- [ ] 2.1 Add skip-and-remind logic to login flow
- [ ] 2.2 Telemetry: 2fa_enrollment_completed / skipped
- [ ] 3.1 Unit tests for TOTP verification
- [ ] 3.2 E2E test: full enrollment happy path
- [ ] 3.3 E2E test: skip-then-prompted-on-next-login
9.4 What the PO sees
Just JIRA-501, its status, the linked PR, and a one-line spec link. They never open the repo unless they want the detail.
9.5 What the dev and the agent see
The full change folder. The agent reads the repo; Jira mostly gets status updates back. Keep it that thin on purpose.
10. Decision Matrix
| Situation | Strategy |
|---|---|
| Solo dev / small team, no external stakeholders | Skip Jira; OpenSpec alone is enough |
| Small-to-mid team with a PO, standard Jira Free/Standard | Strategy D — what/how separation |
| Team using Jira heavily for portfolio reporting | Strategy D + Components/Labels for domains |
| Enterprise on Jira Premium, SAFe adopters | Strategy C — Initiative → Epic = one change (Advanced Roadmaps) |
| Long-running capability with many small changes | Strategy B — Domain as Epic, with caution |
| Regulated environment with audit requirements | Strategy D + SpecFact or custom sync + approval gates in both systems |
11. Summary
- OpenSpec owns specification and implementation detail. Jira owns delivery and coordination. Different jobs.
- Default to Strategy D: one Epic per initiative, one Story or Task per change folder, short acceptance criteria that point at the delta spec,
tasks.mdonly in git. - Mirroring
tasks.mdas Sub-tasks buys noise and drift; skip it. - Link both ways: Story points at the folder; proposal header cites the ticket.
- Automate boring status moves (MCP, Jira Automation). Humans should not click through the same transition twice.
- Behavior: spec wins. “Done, who, when, which sprint”: Jira wins.
SDD and Jira are fine together if behavior stays in the repo and Jira stays thin: who, when, sprint, no pasted tasks.md.