feat(atlassian): add the Atlassian connector — Jira verify + Confluence publish [roadmap:atlassian]#11
Merged
tcballard merged 9 commits intoJul 2, 2026
Conversation
…ADR-011) [roadmap:atlassian] ADR-010 fixes the TicketVerifier/PagePublisher sibling seams (ADR-003 precedent), the no-SDK httpx client, Cloud-first auth, bulkfetch reads, the nested CLI verbs, and the exit-code contract. ADR-011 fixes Confluence page identity (content property, never title), hash-based idempotency, and version-checked never-forced updates. The design rac/designs/atlassian-connector-shape.md works the module and client shape.
…roadmap:atlassian] The --graph contract already emits external: true on external-target edges (related_tickets, rac-core ADR-087; verified_by, rac-core ADR-096) and the configured ticketing provider on ticket edges (ADR-074). Parse both additively with tolerant defaults so the verify verb can select Jira edges by contract markers instead of guessing from key shapes.
…:atlassian] An internal client over httpx — no Atlassian SDK (ADR-010) — implementing the JiraClient and ConfluenceClient Protocols against Cloud: Basic auth from ATLASSIAN_BASE_URL/EMAIL/API_TOKEN, bulkfetch with narrow fields, globalId remote-link upsert, property-keyed page lookup (ADR-011), version-checked updates with 409 -> VersionConflictError, and capped jittered backoff honouring Retry-After on 429/5xx. Ships as the [atlassian] extra; the wire tests run through httpx.MockTransport and importorskip without it.
… edges [roadmap:atlassian] The read-only verify verb (ADR-010): selects --graph edges marked external with provider jira, dedupes issue keys from bare references and browse URLs, batches 100 at a time through bulk fetch, and classifies each as exists (with statusCategory), missing, or forbidden — attributed back to the source artifacts. TicketVerifier/PagePublisher seams and VerifySummary join base.py beside the existing seams; the CLI gains the nested 'atlassian verify' verb with exit 3 on findings.
…s [roadmap:atlassian] Fake-client batteries: edge selection (github and verified_by skipped by construction), key dedupe with multi-artifact attribution, the 100-key batch boundary, permission-hinted and whole-batch auth failures as forbidden, credential-free dry-run, and the 0/1/2/3 CLI exit-code contract including the contract-major warning.
…p:atlassian] A deliberately small deterministic subset (headings, paragraphs, emphasis, inline code, fenced code as the code macro, flat lists, links), rendered escape-first because corpus content is untrusted input (rac-core ADR-065): hostile HTML and macro text stay inert, CDATA cannot break out of code blocks, and only http/https/mailto links become anchors. body_hash over the rendered bytes is the publish idempotency signal (ADR-011); golden tests pin the output byte-for-byte.
…identity [roadmap:atlassian] The publish verb (ADR-011): pages are keyed by the lore.artifact_id content property plus the lore-managed label — never the title, never id write-back into the corpus. Unchanged body hashes skip without a write (a second publish over an unchanged corpus performs zero writes); changed pages update version-checked, and a 409 conflict is surfaced and left alone. The CLI gains 'atlassian publish' with --space / ATLASSIAN_CONFLUENCE_SPACE and the standard malformed-line skip / --strict handling.
Quickstart, the verify exit-code table (including 3, the CI gate), the page-identity and idempotency model, the escape-first rendering notes, the named deferrals, and the live smoke-test checklist that gates any release tag. README connectors region resynced (new 'Workspace & ticketing' kind group); CHANGELOG gains the Unreleased entry.
tcballard
merged commit Jul 2, 2026
a2dcfaf
into
claude/repo-topology-convergence-2a1awb
4 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
rac/roadmaps/atlassian-connector.md(epic #4; sub-issues #5–#9).Adds:
rac-connect atlassian verify— read-only Jirarelated_ticketsexistence/state checks overrac export --graph, exit 3 on findings (the CI gate)rac-connect atlassian publish— idempotent managed Confluence pages overrac export --documents, keyed by a content property, never the titleTicketVerifier/PagePublisherseams,VerifySummary, the[atlassian]extra (an internalhttpxclient, no Atlassian SDK), the additiveGraphEdge.external/providercontract fields, 30 offline tests, the docs page with the release-gating smoke checklist, ADR-010/ADR-011, and the design + roadmap artifactsRoadmap / ADR Trace
Roadmap:
rac/roadmaps/atlassian-connector.md(this repo, delivery track)rac/roadmaps/atlassian/atlassian.md(rac-core, suite intent — companion PR docs(roadmap): add the atlassian suite roadmap [roadmap:atlassian] core#249)Relevant ADRs:
rac/decisions/adr-010-atlassian-verify-publish-seams.md(new)rac/decisions/adr-011-confluence-page-identity-and-idempotency.md(new)rac/decisions/adr-002-connector-interface.md,adr-003-graph-connector-seam-neo4j.md(the sibling-seam precedent)Scope
Included
external+provider == "jira"); key extraction from bare references and/browse/URLs; dedupe with multi-artifact attribution; 100-keybulkfetchbatches withfields=["status"]; exists/missing/forbidden classification incl. statusCategory; credential-free--dry-runlore.artifact_idcontent property +lore-managedlabel; sha256 body-hash skip (second publish over an unchanged corpus performs zero writes); version-checked updates, 409 surfaced as a skip and never forced; deterministic escape-first Markdown-to-storage rendering (hostile HTML/macros stay inert, CDATA break-out blocked, only http/https/mailto links become anchors)ATLASSIAN_BASE_URL/ATLASSIAN_EMAIL/ATLASSIAN_API_TOKEN; capped jittered backoff honouringRetry-Afteron 429/5xx; 401/403 fail fast without retry.[dev,atlassian]so the mock-transport wire tests run (theyimportorskiphttpx, so a bare.[dev]install still passes)Excluded
--jsonverify output (exit code is the day-one machine contract)Product / Architecture Decisions
verifyis read-shaped and returns a report, never records — the shape keeps the recall/re-rank surface ADR-002 rejects structurally impossible. This extends the seam family exactly the way ADR-003 did for graphs; ADR-002 is not reopened.httpxclient is a smaller supply chain than a wrapper library and the wire behaviour (backoff, conflict handling) is the part worth testing. Jira Cloud's classic/searchendpoint no longer exists — the client usesbulkfetch; search, if ever needed, isPOST /search/jql.rac-connect atlassian verify|publish): the suite grows verbs (comment-mode, ingest) and a flatatlassian-verifywould fork the backend namespace.User-Facing Contract
CLI
Human Output
Per-reference lines (
exists PROJ-1: Done (done) <- RAC-…,missing PROJ-404: … <- RAC-…) — findings always print, even without--verbose; summary line to stderr (atlassian verify: 2 checked, 1 exist, 1 missing, 0 forbidden, 2 skipped). Publish action lines:created page … / updated page … / skip …: unchanged / version conflict … not overwritten.JSON Output
None added. The graph input contract now surfaces
edges[].externalandedges[].providerin the reader (additive, tolerant defaults; emitted by rac-core since v0.25.0).Exit Codes
0: verify — all checked references exist; publish — done1: malformed--graphpayload / malformed JSONL line under--strict2: credentials missing (each absentATLASSIAN_*variable named); publish also: no space configured3: verify only — one or more references missing or forbiddenVerification
Ran
python -m pytest -q— 192 passed (was 162; +30: graph marker parsing, verify behaviour, publish behaviour, render goldens, client wire contract, CLI verbs)python -m ruff check src/ tests/ scripts/,ruff format --check,python -m mypy src/— cleanpython scripts/sync_readme.py --check— in sync (8 connectors; new "Workspace & ticketing" group, no script change needed — the grouping is data-driven)rac validate rac/,rac relationships rac/ --validate— exit 0;rac review rac/— 100/100, nothing needs attentionrac export rac/ --graph | rac-connect atlassian verify --dry-run— 7 github-provider tickets correctly skipped, exit 0;rac export rac/ --documents | rac-connect atlassian publish --dry-run— 15 pages planned, exit 0Covered
verified_by(provider-null) edges skipped by construction; resolved in-corpus edges ignored; key dedupe with multi-artifact attribution; the 101-keys-to-2-batches boundary; permission-hinted per-key errors and whole-batch auth failures classified forbidden; unparseable targets skipped not checked; empty graph exits 0; dry-run makes zero client callsac:structured-macrotext, and CDATA]]>break-outs stay inert;javascript:links stay textRetry-Afterhonoured with a capped 4-retry budget then surfaced; 401/403 fail fast with no retry; 409 raisesVersionConflictError; every missing env var named--strict,--input, missing-space errorReview Path
rac/decisions/adr-010…,adr-011…,rac/designs/atlassian-connector-shape.md— the contractsrc/rac_connectors/graph.py,base.py— the additive contract/seam layersrc/rac_connectors/atlassian/client.py— auth, backoff, the six endpointsjira.py,render.py,confluence.py,connector.py— the two verbscli.py— nested verb wiring and exit codesdocs/connectors/atlassian.md+ README region + CHANGELOGNotes For Reviewer
render.pyis the security-sensitive file: corpus content is untrusted input (rac-core ADR-065), and the escape-first discipline plus the CDATA and link-scheme guards are what keep a hostile artifact from smuggling markup into Confluence. The golden tests pin it.forbiddenfindings (exit 3) rather than exit 2 — exit 2 is reserved for missing configuration; a rejected credential is instance state worth surfacing per reference.Implementation Process
Implemented with AI assistance under the roadmap contract; final scope, review, and acceptance decisions rest with the maintainer.