Skip to content

Add Jira and Azure DevOps issue backends (#81, #82) - #83

Merged
laird merged 5 commits into
masterfrom
claude/jira-ticket-fcr8s7
Jul 29, 2026
Merged

Add Jira and Azure DevOps issue backends (#81, #82)#83
laird merged 5 commits into
masterfrom
claude/jira-ticket-fcr8s7

Conversation

@laird

@laird laird commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Adds two first-class issue backends alongside file and github, each implementing the same uniform 9-verb contract (list/get/update/comment/close/create/claim/release/any-claimable) with the same gh-shaped JSON output and exit codes, so the rest of the workflow never branches on which tracker is in use.

Closes #81
Closes #82

Jira (jira)

  • plugins/autocoder/scripts/issues-jira.sh — REST API v2. Keys PROJ-N map to the numeric suffix as number; labels ↔ Jira labels; state via statusCategory + transitions. Non-secret baseUrl/project from a jira object in .autocoder.json; credentials env-only (JIRA_EMAIL + JIRA_API_TOKEN, or JIRA_AUTH_HEADER for Server/DC).
  • Claimable JQL ORs in labels is EMPTY so unlabeled issues aren't dropped — the Jira analogue of the GitHub no:label bug (issues-gh.sh list uses invalid no:label search syntax — autonomous loop sees zero issues #57).

Azure DevOps (ado)

  • plugins/autocoder/scripts/issues-ado.sh — Work Item Tracking REST API. Work-item IDs are integers, so number = System.Id; labels ↔ Tags (System.Tags); state via System.State (done-state set covers the default Agile/Basic/Scrum/CMMI processes). WIQL for list/any-claimable, workitemsbatch for details, JSON-patch for create/update. Non-secret orgUrl/project from an ado object; PAT env-only (ADO_PAT); type/state names env-overridable.
  • WIQL's [System.Tags] NOT CONTAINS 'x' already matches tag-less items, so untagged work stays claimable with no special clause.

Wiring

  • Both backends registered in the plugins/ dispatcher and the .agent/ mirror, issue-source-lib.sh validation, /set-issue-source (both platforms), and launcher help strings.
  • Secrets are never written to .autocoder.json or committed — only non-secret connection settings live there.

Tests — hermetic, green in CI (no network)

Backend curl-stub (request shape) in-process fake (lifecycle)
Jira test_issues_jira.sh (35) test_issues_jira_integration.sh (17) + fixtures/fake_jira.py
Azure DevOps test_issues_ado.sh (28) test_issues_ado_integration.sh (18) + fixtures/fake_ado.py

The integration fakes maintain state and evaluate the query language (JQL / WIQL), so the full create → get → claim/release → comment → update → close lifecycle and every state filter run over real HTTP. Both auto-run in the existing CI shell job. Full shell suite: 17/17 green locally and in CI (run #135 on the head commit).

For a check against a real Jira, plugins/autocoder/scripts/jira-smoke-test.sh runs the lifecycle with JIRA_* set (kept out of CI since it needs egress).

Docs & versioning

  • docs/issue-backends.md (overview of all four backends), docs/jira-setup.md, docs/ado-setup.md; CLAUDE.md / GEMINI.md / README updated.
  • Version bumps: autocoder 4.5.2 → 4.7.0, marketplace 3.26.0 → 3.28.0.

Notes for review

🤖 Generated with Claude Code


Generated by Claude Code

claude added 5 commits July 28, 2026 23:22
Adds a first-class `jira` issue source alongside `file` and `github`,
implementing the same uniform 9-verb backend contract (list, get, update,
comment, close, create, claim, release, any-claimable) against the Jira
REST API v2.

- issues-jira.sh: self-contained backend. Maps Jira issues (PROJ-N) to the
  gh-compatible JSON shape, exposing the numeric key suffix as `number` so it
  stays compatible with the swarm manifest and the rest of the workflow.
  Uses API v2 for plain-text descriptions. Non-secret connection settings
  (baseUrl, project) come from the `jira` object in .autocoder.json;
  credentials are env-only (JIRA_EMAIL + JIRA_API_TOKEN, or JIRA_AUTH_HEADER
  for Server/DC PATs) and never committed.
- Claimable/open JQL ORs in `labels is EMPTY` so label-less issues are not
  silently dropped — the Jira analogue of the GitHub `no:label` bug (#57).
- Registers `jira` in both dispatchers (plugins + .agent mirror),
  issue-source-lib.sh validation, and the set-issue-source command.
- test_issues_jira.sh: 35 assertions covering JQL, request shaping, output
  schema, and exit codes, with curl fully stubbed (no network).
- Updates CLAUDE.md/GEMINI.md, launcher help strings, and bumps autocoder
  4.5.2 -> 4.6.0 and the marketplace 3.26.0 -> 3.27.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv
The unit test (test_issues_jira.sh) stubs curl and asserts request/JQL
shape; this adds real end-to-end coverage without any network:

- tests/fixtures/fake_jira.py: a small stdlib-only STATEFUL fake of the
  Jira REST API v2 covering exactly the endpoints issues-jira.sh calls
  (search, issue CRUD, comment, transitions, assignee, myself). It keeps
  issues in memory and evaluates the JQL the backend emits, so state
  filters and the lifecycle behave like real Jira. Binds an ephemeral
  loopback port (printed as "LISTENING <port>") so CI runs never collide.
- tests/test_issues_jira_integration.sh: starts the fake, exercises the
  full lifecycle (create → get → claim/release → comment → update → close)
  over real HTTP and asserts every state filter — including the label-less
  "labels is EMPTY" guard a stateless mock cannot exercise — then tears the
  server down. Skips cleanly if curl/python3 are absent. 17 assertions.
- plugins/autocoder/scripts/jira-smoke-test.sh: opt-in check against a REAL
  Jira instance (JIRA_* env). Kept out of tests/ so CI never needs egress.
- CLAUDE.md: document the three test layers.

Full shell suite green (15/15); server teardown leaves no stray process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv
Walks through creating a free Jira Cloud site, project, and API token;
wiring the non-secret baseUrl/project into .autocoder.json while keeping
credentials in the environment; verifying with jira-smoke-test.sh; and the
two hermetic CI tests. No secrets in the doc. Linked from CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv
Adds a first-class `ado` issue source implementing the same uniform 9-verb
backend contract against the Azure DevOps Work Item Tracking REST API.

- issues-ado.sh: self-contained backend. Work item IDs are integers, so
  `number` maps to System.Id directly. Labels ↔ work-item Tags
  (System.Tags), state ↔ System.State (done-state set covers the default
  Agile/Basic/Scrum/CMMI processes). Uses WIQL for list/any-claimable,
  workitemsbatch for details, and json-patch for create/update. Non-secret
  orgUrl/project come from the `ado` object in .autocoder.json; the PAT is
  env-only (ADO_PAT) and never committed. Overridable work-item type and
  open/closed state names via env.
- WIQL `[System.Tags] NOT CONTAINS 'x'` already matches tag-less items, so
  untagged work stays claimable without a special empty clause (the ADO
  analogue of the GitHub no:label / Jira empty-labels traps) — asserted by
  both tests.
- Registers `ado` in both dispatchers (plugins + .agent mirror),
  issue-source-lib validation, the set-issue-source command, and launcher
  help strings.
- Tests (both hermetic, in CI): test_issues_ado.sh (28 assertions, stubs
  curl; WIQL + json-patch shape, schema, exit codes) and
  test_issues_ado_integration.sh (18 assertions; real HTTP against a stateful
  fake, tests/fixtures/fake_ado.py, full lifecycle + WIQL filtering).
- Docs: docs/ado-setup.md; updates CLAUDE.md/GEMINI.md. Bumps autocoder
  4.6.0 -> 4.7.0 and the marketplace 3.27.0 -> 3.28.0.

Full shell suite green (17/17).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv
- docs/issue-backends.md: unified overview of the four built-in backends —
  the 9-verb contract, uniform JSON shape and exit codes, per-backend model
  mapping, secrets handling, configuration precedence, and the hermetic
  test layers. Links to the Jira and ADO setup guides.
- README: refresh the "Issue Backends" section — full 9-verb contract table
  (adds claim/release/any-claimable), point at the new docs, and use a
  genuinely custom name (linear) for the custom-backend example now that
  jira/ado are first-class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv
@laird
laird merged commit a4cc38b into master Jul 29, 2026
4 checks passed
laird added a commit that referenced this pull request Jul 29, 2026
Two gaps found by a dry-run of the full merge chain.

1. issue-source-lib.sh existed only under plugins/autocoder/scripts/.
   It is issue-source resolution used by the swarm launch/lifecycle
   scripts — shared issue-layer infrastructure, not platform-specific —
   so the mirror was incomplete. Copied verbatim.

2. The parity guard's SHARED list covered only the two original backends
   (file, gh). issues-jira.sh and issues-ado.sh arrived with #83 and were
   never checked, and neither was issue-source-lib.sh. A backend that
   ships only under plugins/ is invisible to the guard — which is exactly
   how the drift in #71 went unnoticed: the mirror looked healthy because
   nothing checked the files missing from it. SHARED now lists the whole
   issue layer (7 files).

Verification: parity guard 19/19, up from 13/13. Negative-tested both new
assertion classes — removing issue-source-lib.sh from the mirror fails 2
assertions, and perturbing issues-jira.sh fails the divergence check — so
the added coverage is not vacuous. 18/18 suites pass, build clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
laird added a commit that referenced this pull request Aug 8, 2026
…er (jira/ado) (#87)

* Add Jira issue backend parallel with GitHub (#81)

Adds a first-class `jira` issue source alongside `file` and `github`,
implementing the same uniform 9-verb backend contract (list, get, update,
comment, close, create, claim, release, any-claimable) against the Jira
REST API v2.

- issues-jira.sh: self-contained backend. Maps Jira issues (PROJ-N) to the
  gh-compatible JSON shape, exposing the numeric key suffix as `number` so it
  stays compatible with the swarm manifest and the rest of the workflow.
  Uses API v2 for plain-text descriptions. Non-secret connection settings
  (baseUrl, project) come from the `jira` object in .autocoder.json;
  credentials are env-only (JIRA_EMAIL + JIRA_API_TOKEN, or JIRA_AUTH_HEADER
  for Server/DC PATs) and never committed.
- Claimable/open JQL ORs in `labels is EMPTY` so label-less issues are not
  silently dropped — the Jira analogue of the GitHub `no:label` bug (#57).
- Registers `jira` in both dispatchers (plugins + .agent mirror),
  issue-source-lib.sh validation, and the set-issue-source command.
- test_issues_jira.sh: 35 assertions covering JQL, request shaping, output
  schema, and exit codes, with curl fully stubbed (no network).
- Updates CLAUDE.md/GEMINI.md, launcher help strings, and bumps autocoder
  4.5.2 -> 4.6.0 and the marketplace 3.26.0 -> 3.27.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv

* Add hermetic Jira integration test with a stateful fake server

The unit test (test_issues_jira.sh) stubs curl and asserts request/JQL
shape; this adds real end-to-end coverage without any network:

- tests/fixtures/fake_jira.py: a small stdlib-only STATEFUL fake of the
  Jira REST API v2 covering exactly the endpoints issues-jira.sh calls
  (search, issue CRUD, comment, transitions, assignee, myself). It keeps
  issues in memory and evaluates the JQL the backend emits, so state
  filters and the lifecycle behave like real Jira. Binds an ephemeral
  loopback port (printed as "LISTENING <port>") so CI runs never collide.
- tests/test_issues_jira_integration.sh: starts the fake, exercises the
  full lifecycle (create → get → claim/release → comment → update → close)
  over real HTTP and asserts every state filter — including the label-less
  "labels is EMPTY" guard a stateless mock cannot exercise — then tears the
  server down. Skips cleanly if curl/python3 are absent. 17 assertions.
- plugins/autocoder/scripts/jira-smoke-test.sh: opt-in check against a REAL
  Jira instance (JIRA_* env). Kept out of tests/ so CI never needs egress.
- CLAUDE.md: document the three test layers.

Full shell suite green (15/15); server teardown leaves no stray process.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv

* Add docs/jira-setup.md — Jira backend setup & testing guide

Walks through creating a free Jira Cloud site, project, and API token;
wiring the non-secret baseUrl/project into .autocoder.json while keeping
credentials in the environment; verifying with jira-smoke-test.sh; and the
two hermetic CI tests. No secrets in the doc. Linked from CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv

* Add Azure DevOps issue backend parallel with GitHub/Jira (#82)

Adds a first-class `ado` issue source implementing the same uniform 9-verb
backend contract against the Azure DevOps Work Item Tracking REST API.

- issues-ado.sh: self-contained backend. Work item IDs are integers, so
  `number` maps to System.Id directly. Labels ↔ work-item Tags
  (System.Tags), state ↔ System.State (done-state set covers the default
  Agile/Basic/Scrum/CMMI processes). Uses WIQL for list/any-claimable,
  workitemsbatch for details, and json-patch for create/update. Non-secret
  orgUrl/project come from the `ado` object in .autocoder.json; the PAT is
  env-only (ADO_PAT) and never committed. Overridable work-item type and
  open/closed state names via env.
- WIQL `[System.Tags] NOT CONTAINS 'x'` already matches tag-less items, so
  untagged work stays claimable without a special empty clause (the ADO
  analogue of the GitHub no:label / Jira empty-labels traps) — asserted by
  both tests.
- Registers `ado` in both dispatchers (plugins + .agent mirror),
  issue-source-lib validation, the set-issue-source command, and launcher
  help strings.
- Tests (both hermetic, in CI): test_issues_ado.sh (28 assertions, stubs
  curl; WIQL + json-patch shape, schema, exit codes) and
  test_issues_ado_integration.sh (18 assertions; real HTTP against a stateful
  fake, tests/fixtures/fake_ado.py, full lifecycle + WIQL filtering).
- Docs: docs/ado-setup.md; updates CLAUDE.md/GEMINI.md. Bumps autocoder
  4.6.0 -> 4.7.0 and the marketplace 3.27.0 -> 3.28.0.

Full shell suite green (17/17).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv

* Document all issue backends (file, GitHub, Jira, Azure DevOps)

- docs/issue-backends.md: unified overview of the four built-in backends —
  the 9-verb contract, uniform JSON shape and exit codes, per-backend model
  mapping, secrets handling, configuration precedence, and the hermetic
  test layers. Links to the Jira and ADO setup guides.
- README: refresh the "Issue Backends" section — full 9-verb contract table
  (adds claim/release/any-claimable), point at the new docs, and use a
  genuinely custom name (linear) for the custom-backend example now that
  jira/ado are first-class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015zJKDkbj54vZeGaTWLJEHv

* Complete the .agent issue-layer mirror and widen the parity guard (#71)

Two gaps found by a dry-run of the full merge chain.

1. issue-source-lib.sh existed only under plugins/autocoder/scripts/.
   It is issue-source resolution used by the swarm launch/lifecycle
   scripts — shared issue-layer infrastructure, not platform-specific —
   so the mirror was incomplete. Copied verbatim.

2. The parity guard's SHARED list covered only the two original backends
   (file, gh). issues-jira.sh and issues-ado.sh arrived with #83 and were
   never checked, and neither was issue-source-lib.sh. A backend that
   ships only under plugins/ is invisible to the guard — which is exactly
   how the drift in #71 went unnoticed: the mirror looked healthy because
   nothing checked the files missing from it. SHARED now lists the whole
   issue layer (7 files).

Verification: parity guard 19/19, up from 13/13. Negative-tested both new
assertion classes — removing issue-source-lib.sh from the mirror fails 2
assertions, and perturbing issues-jira.sh fails the divergence check — so
the added coverage is not vacuous. 18/18 suites pass, build clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

Add Azure DevOps issue support Add Jira ticket support

2 participants