Skip to content

feat: GitHub-based JIRA YAML workflow#1553

Draft
jonsnyder wants to merge 11 commits into
mainfrom
github-jira-yaml-workflow
Draft

feat: GitHub-based JIRA YAML workflow#1553
jonsnyder wants to merge 11 commits into
mainfrom
github-jira-yaml-workflow

Conversation

@jonsnyder

Copy link
Copy Markdown
Collaborator

Summary

  • Adds an OpenSpec change (openspec/changes/github-jira-yaml-workflow/) proposing a system for managing JIRA tickets as versioned YAML files in .jira/
  • YAML files (one per ticket) hold a details snapshot and an updates array of idempotent REST calls applied on PR merge
  • scripts/jira/apply.mjs <filename> applies updates and prints the resolved ticket key to stdout; scripts/jira/fetch.mjs <key> <filename> refreshes the details section from live JIRA data
  • The existing version-and-publish.yml build workflow gains an apply-jira job that detects changed .jira/*.yml files, runs apply → fetch on each, and commits refreshed details back to main
  • A new /jira-propose Claude Code skill creates or updates .jira/ files locally from git context — no JIRA API calls made locally

Artifacts

Artifact Description
proposal.md Why and what changes
design.md Key decisions: YAML-over-JSON, REST call array, remote-link idempotency, no separate workflow file
specs/jira-yaml-schema File naming, details/updates structure, set idempotency
specs/jira-apply-workflow Build job integration, per-file apply→fetch loop, remote-link creation
specs/jira-fetch-script Two required args (key + filename), details refresh, preserves updates
specs/jira-propose-action Local-only propose skill, git context inference, ISSUE_TEMPLATES defaults
tasks.md 26 checkboxed tasks across 6 groups

Test plan

  • Review proposal, design, and specs for accuracy and completeness
  • Verify tasks cover all spec requirements
  • Run /opsx:apply to begin implementation when ready

🤖 Generated with Claude Code

Proposes a system for managing JIRA tickets as versioned YAML files in
.jira/, applied to JIRA automatically on PR merge via the existing build
workflow. Includes specs and tasks for apply.mjs, fetch.mjs, /jira-propose
skill, and build workflow integration.
@changeset-bot

changeset-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: ef9e592

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Comment thread openspec/changes/github-jira-yaml-workflow/proposal.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/tasks.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/specs/jira-propose-action/spec.md Outdated
- Workflow: loop runs apply→delete→fetch per changed file (not update-in-place)
- Use repo-{PR#} as remote link global ID for reliable JIRA indexing; no pagination needed
- XXXX files deleted and replaced by real-key files after apply succeeds
- fetch.mjs requires both key AND filename as required args
- Apply script exits 0 for no-updates files (triggering delete+fetch)
- Details section refreshed by build workflow via fetch, not read-only
- JIRA/changeset changes land in a single skip-ci commit
- Skill path: .claude/skills/jira-propose/SKILL.md (not commands/)
- Add quality gate: PRs must have a .jira/ YAML file
- Add PR comment step after apply listing updated tickets
- Propose description includes business value (customer names, elevator pitch)
- fetch can read any JIRA project; PDCL creation only restriction
- Resolved open questions; removed stale risks

@jonsnyder jonsnyder left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Submitting pending review to unblock replies.

Comment thread openspec/changes/github-jira-yaml-workflow/proposal.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/proposal.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/design.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/design.md
Comment thread openspec/changes/github-jira-yaml-workflow/design.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/tasks.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/tasks.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/tasks.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/tasks.md Outdated
Comment thread openspec/changes/github-jira-yaml-workflow/tasks.md Outdated
claude added 2 commits June 26, 2026 11:08
Adds the full system for version-controlling JIRA changes as YAML files
in .jira/ and applying them automatically on merge to main.

- .jira/.gitkeep + README.md with schema docs, custom field reference,
  and end-to-end workflow description
- scripts/jira/fetch.mjs: fetches JIRA issue state into details section
  (two required args: ticket-key and filename; --dry-run supported)
- scripts/jira/apply.mjs: executes updates[] array against JIRA REST API,
  creates remote link with globalId repo-{PR#} for idempotency, prints
  resolved ticket key to stdout (--dry-run supported)
- .github/workflows/version-and-publish.yml: new apply-jira job runs on
  every push to main; detects changed .jira/*.yml files, runs apply then
  fetch on each, commits refreshed details, posts PR comment with ticket
  links; publish job now depends on apply-jira with git pull --rebase
- .claude/skills/jira-propose/SKILL.md: skill for proposing ticket
  changes locally from git context, no JIRA API calls
- CLAUDE.md: project guide with workflow, scripts, and custom field ref
- README.md: added JIRA workflow section

Task 6.2 (add JIRA_API_TOKEN to Production environment) requires
manual action in GitHub repository settings.
@jonsnyder jonsnyder changed the title feat: GitHub-based JIRA YAML workflow (OpenSpec proposal) feat: GitHub-based JIRA YAML workflow Jun 26, 2026
Comment thread scripts/jira/apply.mjs Outdated
Comment thread scripts/jira/apply.mjs Outdated
Comment thread scripts/jira/apply.mjs Outdated
Comment thread .github/workflows/version-and-publish.yml Outdated
claude added 2 commits June 26, 2026 12:21
…d per-ticket

Addresses four review comments on PR #1553:

- api.mjs: extract all JIRA HTTP calls into a factory (createApi) with
  dryRun, baseUrl, and token injected — enables full unit testing without
  vi.mock()
- apply.mjs / fetch.mjs: converted to exported functions (applyFile,
  fetchFile) that accept an injected api object; isMain guard added for
  CLI entry point
- process.mjs: new script that orchestrates apply + fetch for a single
  file, handles XXXX→real-key rename, and short-circuits on missing file
  or empty updates — replaces 30+ lines of inline bash in the workflow
- globalId: moved from auto-generated `repo-{PR#}` to a per-ticket
  unique hex string specified in the YAML remotelink entry, enabling
  multiple new tickets per PR merge
- When a re-run finds an existing ticket via globalId search, the create
  fields are PUT to that ticket instead of creating a duplicate
- apply.spec.js / fetch.spec.js / process.spec.js: new test files (78
  tests passing) using injected mock APIs
- jira-propose SKILL.md: updated template to include the remotelink
  entry with a unique generated globalId
- workflow: bash per-file logic replaced with `node scripts/jira/process.mjs "$file"`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… imports

- Renamed api/apply/fetch/process from .mjs to .js to match repo convention
  (package.json has "type": "module" so .js files are already ESM)
- Switched from default import to named imports for js-yaml v5 compatibility:
  `import { load as yamlLoad, dump as yamlDump } from "js-yaml"`
- Added js-yaml as an explicit root devDependency (was previously a hoisted
  transitive dep from read-yaml-file@js-yaml@3.x)
- Updated workflow and CLAUDE.md script references from .mjs to .js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Comment thread scripts/jira/api.js Outdated
Comment thread scripts/jira/fetch.js Outdated
Comment thread scripts/jira/fetch.js Outdated
Comment thread scripts/jira/process.js Outdated
Comment thread CLAUDE.md Outdated
claude added 3 commits June 26, 2026 14:40
Resolved pnpm-lock.yaml merge conflict by regenerating via pnpm install.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…always required

Addresses five review comments:

- api.js: dryRun now only blocks mutation requests (non-GET); searchIssues
  and getRemoteLinks always execute so dry-run previews reflect real JIRA state
- fetch.js: fetchFile always fetches from JIRA; in dryRun outputs the YAML
  content it would write to stdout instead of skipping the fetch entirely
- fetch.js: removed existing-file check — fetch always overwrites with just
  the details section (updates have already been applied at this point)
- process.js / apply.js / fetch.js: JIRA_API_TOKEN is now required even in
  dry-run mode since read queries need authentication
- CLAUDE.md: removed (not ready to commit yet)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Satisfies ESLint func-style rule; removes unused beforeEach import from
apply.spec.js; fixes no-await-in-loop in apply.js with Promise.all for
parallel remote-link fetching and eslint-disable for sequential updates.
Comment thread scripts/jira/apply.js Outdated
import { fileURLToPath } from "url";
import { load as yamlLoad } from "js-yaml";
import createApi from "./api.js";
import { JIRA_BASE_URL, JIRA_API_TOKEN } from "../team/config.js";

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Do not rely on team/config.js, and remove it locally too. Just make a config.js file in scripts/jira

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done — created scripts/jira/config.js with just JIRA_BASE_URL and JIRA_API_TOKEN. All four jira scripts now import from ./config.js instead of ../team/config.js.

Comment thread scripts/jira/apply.js
@@ -0,0 +1,186 @@
#!/usr/bin/env node

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I want to make this script more dumb. Let's make these changes to the workflow and update all necessary files:

new tickets are not PDCL-XXXX-...yml. Instead of the XXXX they should have the global id (the short random string).
use the global id as a label on the new ticket.
The apply script should:

  1. extract the ticket or global id
  2. if global id check for existing ticket number
  3. Loop through all updates:
  4. if update is POST, run if no ticket number
  5. if update is PUT, run if have ticket number
  6. End loop
  7. Ensure there is a link from the ticket to the pr URL. (For every updated and new JIRA tickets). No need for there to be a remote link update in the yml.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Redesigned as requested:

  • Filename format: new tickets now use PDCL-{globalId}-title.yml (e.g. PDCL-a3f8b2c1-title.yml) — the globalId is an 8-char hex string generated at propose time
  • Label-based idempotency: the globalId goes in labels on the POST create body; apply searches project = PDCL AND labels = "{globalId}" to find existing tickets on re-run
  • Update loop simplified: POST to /rest/api/2/issue runs only when no ticket key yet; all other updates run only when a ticket key is known
  • Auto remote link: apply always creates a remote link from the ticket to the PR at the end (idempotent via PR URL as globalId) — no remotelink entry needed in the YAML
  • Skill updated: jira-propose template no longer includes a remotelink entry; instead it documents the globalId label convention

79 tests passing.

…lId in filename

- Add scripts/jira/config.js; remove team/config.js dependency from all jira scripts
- Rename new-ticket files from PDCL-XXXX-... to PDCL-{globalId}-... pattern
- Apply script now resolves new tickets by searching JIRA label matching the globalId
- Update loop: POST runs only when no ticket key yet; all others run when key is known
- Auto-create remote link to PR for every processed ticket (idempotent via PR URL)
- Remove explicit remotelink entry from jira-propose skill template
- Update openspec tasks/design/proposal to reflect as-built implementation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants