diff --git a/tools/bitbucket/README.md b/tools/bitbucket/README.md index ae6abeedf..ef261f63e 100644 --- a/tools/bitbucket/README.md +++ b/tools/bitbucket/README.md @@ -34,20 +34,22 @@ Bitbucket Cloud and Bitbucket Data Center bridge for Magpie adopters that use Bitbucket as a forge, pull-request review surface, or Jira-paired Atlassian backend. -This bridge implements a `partial` profile for -repository metadata context and pull-request discovery/fetching under -`contract:change-request`, with partial Cloud-only issue reads, comment reads, and attachment metadata reads under -`contract:tracker`. Partial adapters may implement named -contract verbs, but they do not satisfy the complete contract and must -not be advertised as complete/selectable backends. +This bridge implements a `partial` profile for repository metadata +context and pull-request discovery/fetching under +`contract:change-request`. Atlassian fully removed the native Bitbucket +Cloud Issue Tracker API on 2026-08-20, so the existing native issue CLI +surface is retained only as a fail-closed compatibility surface that +directs callers toward linked Jira coverage. Partial adapters may +implement named contract verbs, but they do not satisfy the complete +contract and must not be advertised as complete/selectable backends. Repository metadata reads are currently bridge context for Bitbucket pull-request workflows, not a complete `contract:source-control` -backend. `contract:tracker` coverage is partial and currently includes -Bitbucket Cloud issue listing/fetching where the repository issue tracker is enabled. -#606 remains open for the remaining Bitbucket/Jira workflow coverage. -Later PRs can extend the same adapter with write operations, -linked Jira handoff, issue write operations, and +backend. `contract:tracker` remains partial: native Bitbucket Cloud +issue operations are unavailable after Atlassian's 2026-08-20 removal, +and Data Center native issue operations are also unsupported. +#606 remains open for linked Jira tracker coverage and the remaining +Bitbucket workflow gaps, including broader repository permissions and fuller Pipelines run/log/retry coverage. ## Partial coverage roadmap @@ -56,16 +58,16 @@ The Bitbucket bridge currently provides partial coverage for repository, tracker, and pull-request context, plus narrowly scoped Bitbucket Cloud write operations. It intentionally does not claim full Bitbucket backend parity. -Implemented read-only commands: +Implemented commands and compatibility stubs: - `magpie-bitbucket auth-check` - `magpie-bitbucket repo get` - `magpie-bitbucket repo restrictions` -- `magpie-bitbucket issue list-open` -- `magpie-bitbucket issue get ` -- `magpie-bitbucket issue comments ` -- `magpie-bitbucket issue comment --body-file ` (Cloud-only write) -- `magpie-bitbucket issue attachments ` +- `magpie-bitbucket issue list-open` (fail-closed compatibility stub; native Cloud issue tracker removed) +- `magpie-bitbucket issue get ` (fail-closed compatibility stub; native Cloud issue tracker removed) +- `magpie-bitbucket issue comments ` (fail-closed compatibility stub; native Cloud issue tracker removed) +- `magpie-bitbucket issue comment --body-file ` (fail-closed compatibility stub; native Cloud issue tracker removed) +- `magpie-bitbucket issue attachments ` (fail-closed compatibility stub; native Cloud issue tracker removed) - `magpie-bitbucket pr list-open` - `magpie-bitbucket pr get ` - `magpie-bitbucket pr commits ` @@ -99,12 +101,14 @@ including reviewers, approvals, change-request signals, and related review activity where exposed by the configured Bitbucket backend. Write coverage is intentionally narrow. The bridge supports confirmed -Bitbucket Cloud issue-comment creation, top-level pull-request comment creation, -and pull-request approve/unapprove, request-changes/remove-request-changes, decline, backend-neutral reject, and merge actions after the calling skill has obtained -explicit user confirmation. Other writes, such as editing/deleting comments, -creating/updating issues, changing branches, or triggering builds, remain out -of scope and should be added separately with narrow command surfaces and -maintainer review. +Bitbucket Cloud top-level pull-request comment creation and pull-request +approve/unapprove, request-changes/remove-request-changes, decline, +backend-neutral reject, and merge actions after the calling skill has +obtained explicit user confirmation. Native Bitbucket Cloud issue writes +are unavailable because Atlassian removed the native Issue Tracker API. +Other writes, such as editing/deleting pull-request comments, changing +branches, or triggering builds, remain out of scope and should be added +separately with narrow command surfaces and maintainer review. ## Prerequisites @@ -121,22 +125,18 @@ This implementation primarily covers read-only operations, plus narrowly scoped 1. **Authentication preflight:** verify the configured Bitbucket backend and credentials can reach the selected repository. 2. **Repository metadata:** fetch normalized repository details from Bitbucket Cloud or Data Center. 3. **Repository branch restrictions:** fetch known read-only repository branch restriction / branch permission policy context. -4. **Cloud issue listing:** list open Bitbucket Cloud issues where the repository issue tracker is enabled. -5. **Cloud issue fetch:** fetch one Bitbucket Cloud issue as partial read-only tracker context. -6. **Cloud issue comments fetch:** fetch comments for one Bitbucket Cloud issue as partial read-only tracker context. -7. **Cloud issue attachments fetch:** fetch attachment metadata and links for one Bitbucket Cloud issue as partial read-only tracker context; file contents are not downloaded. -8. **Cloud issue comment create:** create one Bitbucket Cloud issue comment from a confirmed body file; the calling skill is responsible for explicit user confirmation before invoking the bridge. -9. **Pull-request listing:** list open pull requests as `contract:change-request` proposal summaries. -10. **Pull-request fetch:** fetch one pull request as a normalized proposal object. -11. **Pull-request commits fetch:** fetch commits associated with a pull request as normalized read-only output. -12. **Pull-request diff fetch:** fetch the pull request unified diff as normalized read-only output. -13. **Pull-request discussion fetch:** fetch a comments-only pull request discussion subset as normalized read-only output. -14. **Pull-request review-state fetch:** fetch reviewers, approvals, change-request signals, pending review requests, and normalized review activity. -15. **Cloud pull-request approve:** approve one pull request as the authenticated user after explicit caller-side confirmation. -16. **Cloud pull-request unapprove:** withdraw the authenticated user's approval after explicit caller-side confirmation. -17. **Pull-request tasks fetch:** list Bitbucket Cloud pull-request tasks and fetch one task as partial read-only change-request context. -18. **Pull-request merge-check context fetch:** fetch known read-only mergeability, conflict, status-check, and review blocker context while preserving unknown values where the backend does not expose a clear signal. -19. **Pull-request status fetch:** fetch build/status checks for the pull request as normalized read-only output. +4. **Native issue tracker compatibility guard:** retain the historical `issue` CLI surface, but fail closed before any request because Atlassian removed the native Bitbucket Cloud Issue Tracker API on 2026-08-20; Data Center native issues remain unsupported. +5. **Pull-request listing:** list open pull requests as `contract:change-request` proposal summaries. +6. **Pull-request fetch:** fetch one pull request as a normalized proposal object. +7. **Pull-request commits fetch:** fetch commits associated with a pull request as normalized read-only output. +8. **Pull-request diff fetch:** fetch the pull request unified diff as normalized read-only output. +9. **Pull-request discussion fetch:** fetch a comments-only pull request discussion subset as normalized read-only output. +10. **Pull-request review-state fetch:** fetch reviewers, approvals, change-request signals, pending review requests, and normalized review activity. +11. **Cloud pull-request approve:** approve one pull request as the authenticated user after explicit caller-side confirmation. +12. **Cloud pull-request unapprove:** withdraw the authenticated user's approval after explicit caller-side confirmation. +13. **Pull-request tasks fetch:** list Bitbucket Cloud pull-request tasks and fetch one task as partial read-only change-request context. +14. **Pull-request merge-check context fetch:** fetch known read-only mergeability, conflict, status-check, and review blocker context while preserving unknown values where the backend does not expose a clear signal. +15. **Pull-request status fetch:** fetch build/status checks for the pull request as normalized read-only output. The bridge supports two Bitbucket API flavours behind one command surface: @@ -165,8 +165,7 @@ surface: | Change requests | `land` / `pr merge --strategy {merge,squash,rebase} --expected-source-commit ` | Partial write, Cloud only | Submits a Bitbucket Cloud pull-request merge after explicit caller-side confirmation. The caller must run and inspect `pr merge-checks ` before invoking this command; `pr merge` does not independently enforce approval, build-status, or merge-check gates. The expected source commit (7–40 hexadecimal characters, rejected before any request otherwise) is checked immediately before the merge POST so a changed PR head fails closed. The requested strategy is mapped to Bitbucket's merge strategy (reported as `backend_strategy`) and the resulting merge commit is returned as `landed_ref` when available. An asynchronous merge may be accepted before a `landed_ref` is available; `merge_status` is then `submitted`, with Bitbucket's own task state in `task_status` and the task URL in `task_url`. A timeout on the merge POST is reported as "outcome unknown" — check `pr get ` before retrying, since the merge may already be running. Data Center merge writes remain unsupported. | | Change requests | `pr merge-task-status ` | Partial read-only, Cloud only | Fetches the status of an asynchronous Bitbucket Cloud pull-request merge task. Pending tasks normalize to `merge_status=submitted`; successful tasks expose `merge_result.merge_commit.hash` as `landed_ref`; failed tasks normalize to `merge_status=failed`. Data Center merge task-status reads remain unsupported. | | Change requests | `reject` / `pr reject --reason-file ` | Partial write, Cloud only | Implements the backend-neutral reject contract for Bitbucket Cloud after explicit caller-side confirmation. Posts the confirmed rejection reason as a pull-request comment, then declines the pull request. If the decline fails after the reason was posted, the command reports the partial outcome and directs the caller to inspect the PR before retrying to avoid duplicating the rejection comment. Data Center reject writes remain unsupported. | -| Tracker | `issue list-open` / `issue get ` / `issue comments ` / `issue attachments ` | Partial read-only, Cloud only | Lists and fetches Bitbucket Cloud issues, issue comments, and issue attachment metadata/links where the repository issue tracker is enabled. Bitbucket Data Center native issue reads/comments/attachments are unsupported; linked Jira handoff remains separate follow-up work. | -| Tracker | `issue comment --body-file ` | Partial write, Cloud only | Creates one Bitbucket Cloud issue comment from a caller-supplied body file. The calling skill must obtain explicit user confirmation before invoking this mutation. Bitbucket Data Center native issue comment writes are unsupported; linked Jira coverage remains separate. | +| Tracker | `issue list-open` / `issue get ` / `issue comments ` / `issue comment --body-file ` / `issue attachments ` | Removed upstream / fail-closed | Atlassian fully removed the native Bitbucket Cloud Issue Tracker API on 2026-08-20. These commands remain as compatibility stubs and fail before any HTTP request, directing callers toward linked Jira coverage. Bitbucket Data Center native issue operations are also unsupported. | | CI | `pr status ` | Partial read-only | Fetches build/status checks for a pull request. This does not trigger, retry, or mutate Pipelines/builds. | ## Invocation @@ -181,19 +180,13 @@ uv run --project tools/bitbucket magpie-bitbucket repo get # Fetch repository branch restrictions uv run --project tools/bitbucket magpie-bitbucket repo restrictions -# List open Bitbucket Cloud issues +# Native Bitbucket Cloud issue commands are compatibility stubs only. +# Atlassian removed the native Issue Tracker API on 2026-08-20. +# Each command fails closed before any HTTP request and directs callers to Jira. uv run --project tools/bitbucket magpie-bitbucket issue list-open - -# Fetch one Bitbucket Cloud issue uv run --project tools/bitbucket magpie-bitbucket issue get 123 - -# Fetch Bitbucket Cloud issue comments uv run --project tools/bitbucket magpie-bitbucket issue comments 123 - -# Create a Bitbucket Cloud issue comment after caller-side confirmation uv run --project tools/bitbucket magpie-bitbucket issue comment 123 --body-file /tmp/comment.txt - -# Fetch Bitbucket Cloud issue attachment metadata and links uv run --project tools/bitbucket magpie-bitbucket issue attachments 123 # List open pull requests @@ -327,8 +320,8 @@ then invoke the write command. Follow-up PRs can extend this bridge with: -- Bitbucket issue write operations and additional tracker fields. -- Linked Jira issue handoff through `tools/jira/`. +- Linked Jira issue/tracker handoff through `tools/jira/`, replacing the + removed native Bitbucket Cloud Issue Tracker surface. - Remaining pull-request review operations and broader merge coverage. - Broader repository permission reads. - Fuller Bitbucket Pipelines run/log/retry coverage beyond read-only pull-request status reads. diff --git a/tools/bitbucket/src/magpie_bitbucket/cloud.py b/tools/bitbucket/src/magpie_bitbucket/cloud.py index 4b2f76cfe..ab2294ea0 100644 --- a/tools/bitbucket/src/magpie_bitbucket/cloud.py +++ b/tools/bitbucket/src/magpie_bitbucket/cloud.py @@ -102,39 +102,27 @@ def get_repository_restrictions(config: BitbucketConfig) -> dict[str, Any]: return combined -def list_open_issues(config: BitbucketConfig) -> dict[str, Any]: - """List open issues from a Bitbucket Cloud repository.""" - workspace = quote_path(require(config.workspace, "BITBUCKET_WORKSPACE")) - repo_slug = quote_path(require(config.repo_slug, "BITBUCKET_REPO_SLUG")) - url = f"{CLOUD_API_BASE}/repositories/{workspace}/{repo_slug}/issues?q=state%3D%22new%22%20OR%20state%3D%22open%22%20OR%20state%3D%22on%20hold%22" +_NATIVE_ISSUE_TRACKER_REMOVED = ( + "Bitbucket Cloud native issue tracker API was removed by Atlassian " + "on 2026-08-20; use linked Jira coverage instead." +) - combined: dict[str, Any] = { - "values": [], - "paginated": True, - "pages": [], - } - seen_urls = {url} - while url: - page = get_json(url, config) - combined["pages"].append(page) +def _removed_native_issue_tracker() -> BitbucketError: + """Return the fail-closed error for removed native Cloud issue APIs.""" + return BitbucketError(_NATIVE_ISSUE_TRACKER_REMOVED) - values = page.get("values") - if isinstance(values, list): - combined["values"].extend(item for item in values if isinstance(item, dict)) - - url = _validated_next_url(page.get("next"), seen_urls) - return combined +def list_open_issues(config: BitbucketConfig) -> dict[str, Any]: + """Fail closed because Bitbucket Cloud removed its native issue tracker.""" + _ = config + raise _removed_native_issue_tracker() def get_issue(config: BitbucketConfig, issue_id: str) -> dict[str, Any]: - """Fetch one issue from a Bitbucket Cloud repository.""" - workspace = quote_path(require(config.workspace, "BITBUCKET_WORKSPACE")) - repo_slug = quote_path(require(config.repo_slug, "BITBUCKET_REPO_SLUG")) - issue = quote_path(issue_id) - url = f"{CLOUD_API_BASE}/repositories/{workspace}/{repo_slug}/issues/{issue}" - return get_json(url, config) + """Fail closed because Bitbucket Cloud removed its native issue tracker.""" + _ = (config, issue_id) + raise _removed_native_issue_tracker() def create_issue_comment( @@ -142,77 +130,21 @@ def create_issue_comment( issue_id: str, body: str, ) -> dict[str, Any]: - """Create one comment on a Bitbucket Cloud issue.""" - workspace = quote_path(require(config.workspace, "BITBUCKET_WORKSPACE")) - repo_slug = quote_path(require(config.repo_slug, "BITBUCKET_REPO_SLUG")) - issue = quote_path(issue_id) - url = f"{CLOUD_API_BASE}/repositories/{workspace}/{repo_slug}/issues/{issue}/comments" - - comment = post_json( - url, - config, - {"content": {"raw": body}}, - ) - return { - "issue_id": issue_id, - "comment": comment, - } + """Fail closed because Bitbucket Cloud removed its native issue tracker.""" + _ = (config, issue_id, body) + raise _removed_native_issue_tracker() def get_issue_comments(config: BitbucketConfig, issue_id: str) -> dict[str, Any]: - """Fetch comments for a Bitbucket Cloud issue.""" - workspace = quote_path(require(config.workspace, "BITBUCKET_WORKSPACE")) - repo_slug = quote_path(require(config.repo_slug, "BITBUCKET_REPO_SLUG")) - issue = quote_path(issue_id) - url = f"{CLOUD_API_BASE}/repositories/{workspace}/{repo_slug}/issues/{issue}/comments" - - combined: dict[str, Any] = { - "issue_id": issue_id, - "values": [], - "paginated": True, - "pages": [], - } - - seen_urls = {url} - while url: - page = get_json(url, config) - combined["pages"].append(page) - - values = page.get("values") - if isinstance(values, list): - combined["values"].extend(item for item in values if isinstance(item, dict)) - - url = _validated_next_url(page.get("next"), seen_urls) - - return combined + """Fail closed because Bitbucket Cloud removed its native issue tracker.""" + _ = (config, issue_id) + raise _removed_native_issue_tracker() def get_issue_attachments(config: BitbucketConfig, issue_id: str) -> dict[str, Any]: - """Fetch attachment metadata and links for a Bitbucket Cloud issue.""" - workspace = quote_path(require(config.workspace, "BITBUCKET_WORKSPACE")) - repo_slug = quote_path(require(config.repo_slug, "BITBUCKET_REPO_SLUG")) - issue = quote_path(issue_id) - url = f"{CLOUD_API_BASE}/repositories/{workspace}/{repo_slug}/issues/{issue}/attachments" - - combined: dict[str, Any] = { - "issue_id": issue_id, - "values": [], - "paginated": True, - "pages": [], - } - - seen_urls = {url} - while url: - page = get_json(url, config) - combined["pages"].append(page) - - values = page.get("values") - if isinstance(values, list): - combined["values"].extend(item for item in values if isinstance(item, dict)) - - url = _validated_next_url(page.get("next"), seen_urls) - - return combined + """Fail closed because Bitbucket Cloud removed its native issue tracker.""" + _ = (config, issue_id) + raise _removed_native_issue_tracker() def list_open_pull_requests(config: BitbucketConfig) -> dict[str, Any]: diff --git a/tools/bitbucket/tests/test_bitbucket.py b/tools/bitbucket/tests/test_bitbucket.py index 6896d357f..1203e02d7 100644 --- a/tools/bitbucket/tests/test_bitbucket.py +++ b/tools/bitbucket/tests/test_bitbucket.py @@ -2237,39 +2237,31 @@ def test_cli_repo_restrictions_datacenter( @patch("urllib.request.build_opener") -def test_cloud_list_open_issues_follows_next(mock_build_opener: MagicMock, cloud_env: None) -> None: - opener = mock_opener( - mock_build_opener, - { - "values": [{"id": 1, "title": "First issue", "state": "open"}], - "next": "https://api.bitbucket.org/2.0/repositories/apache/magpie/issues?page=2", - }, - {"values": [{"id": 2, "title": "Second issue", "state": "open"}]}, - ) - - result = cloud.list_open_issues(load_config()) +def test_cloud_list_open_issues_removed_without_request( + mock_build_opener: MagicMock, + cloud_env: None, +) -> None: + with pytest.raises( + BitbucketError, + match="native issue tracker API was removed by Atlassian on 2026-08-20", + ): + cloud.list_open_issues(load_config()) - first_request = opener.open.call_args_list[0].args[0] - second_request = opener.open.call_args_list[1].args[0] - assert first_request.full_url == ( - "https://api.bitbucket.org/2.0/repositories/apache/magpie/issues?q=state%3D%22new%22%20OR%20state%3D%22open%22%20OR%20state%3D%22on%20hold%22" - ) - assert second_request.full_url.endswith("/issues?page=2") - assert result["values"] == [ - {"id": 1, "title": "First issue", "state": "open"}, - {"id": 2, "title": "Second issue", "state": "open"}, - ] + mock_build_opener.assert_not_called() @patch("urllib.request.build_opener") -def test_cloud_get_issue_url(mock_build_opener: MagicMock, cloud_env: None) -> None: - opener = mock_opener(mock_build_opener, {"id": 7, "title": "Fix issue"}) - - result = cloud.get_issue(load_config(), "7") +def test_cloud_get_issue_removed_without_request( + mock_build_opener: MagicMock, + cloud_env: None, +) -> None: + with pytest.raises( + BitbucketError, + match="native issue tracker API was removed by Atlassian on 2026-08-20", + ): + cloud.get_issue(load_config(), "7") - request = opener.open.call_args.args[0] - assert request.full_url == "https://api.bitbucket.org/2.0/repositories/apache/magpie/issues/7" - assert result == {"id": 7, "title": "Fix issue"} + mock_build_opener.assert_not_called() def test_datacenter_list_open_issues_unsupported(datacenter_env: None) -> None: @@ -2365,29 +2357,17 @@ def test_cli_issue_get_cloud( @patch("urllib.request.build_opener") -def test_cloud_get_issue_comments_follows_next(mock_build_opener: MagicMock, cloud_env: None) -> None: - opener = mock_opener( - mock_build_opener, - { - "values": [{"id": 101, "content": {"raw": "First comment"}}], - "next": "https://api.bitbucket.org/2.0/repositories/apache/magpie/issues/7/comments?page=2", - }, - {"values": [{"id": 102, "content": {"raw": "Second comment"}}]}, - ) - - result = cloud.get_issue_comments(load_config(), "7") +def test_cloud_get_issue_comments_removed_without_request( + mock_build_opener: MagicMock, + cloud_env: None, +) -> None: + with pytest.raises( + BitbucketError, + match="native issue tracker API was removed by Atlassian on 2026-08-20", + ): + cloud.get_issue_comments(load_config(), "7") - first_request = opener.open.call_args_list[0].args[0] - second_request = opener.open.call_args_list[1].args[0] - assert first_request.full_url == ( - "https://api.bitbucket.org/2.0/repositories/apache/magpie/issues/7/comments" - ) - assert second_request.full_url.endswith("/issues/7/comments?page=2") - assert result["issue_id"] == "7" - assert result["values"] == [ - {"id": 101, "content": {"raw": "First comment"}}, - {"id": 102, "content": {"raw": "Second comment"}}, - ] + mock_build_opener.assert_not_called() def test_datacenter_get_issue_comments_unsupported(datacenter_env: None) -> None: @@ -2454,29 +2434,17 @@ def test_cli_issue_comments_cloud( @patch("urllib.request.build_opener") -def test_cloud_get_issue_attachments_follows_next(mock_build_opener: MagicMock, cloud_env: None) -> None: - opener = mock_opener( - mock_build_opener, - { - "values": [{"id": 201, "name": "screenshot.png"}], - "next": "https://api.bitbucket.org/2.0/repositories/apache/magpie/issues/7/attachments?page=2", - }, - {"values": [{"id": 202, "name": "trace.log"}]}, - ) - - result = cloud.get_issue_attachments(load_config(), "7") +def test_cloud_get_issue_attachments_removed_without_request( + mock_build_opener: MagicMock, + cloud_env: None, +) -> None: + with pytest.raises( + BitbucketError, + match="native issue tracker API was removed by Atlassian on 2026-08-20", + ): + cloud.get_issue_attachments(load_config(), "7") - first_request = opener.open.call_args_list[0].args[0] - second_request = opener.open.call_args_list[1].args[0] - assert first_request.full_url == ( - "https://api.bitbucket.org/2.0/repositories/apache/magpie/issues/7/attachments" - ) - assert second_request.full_url.endswith("/issues/7/attachments?page=2") - assert result["issue_id"] == "7" - assert result["values"] == [ - {"id": 201, "name": "screenshot.png"}, - {"id": 202, "name": "trace.log"}, - ] + mock_build_opener.assert_not_called() def test_datacenter_get_issue_attachments_unsupported(datacenter_env: None) -> None: @@ -2733,33 +2701,21 @@ def test_cli_pr_task_cloud( @patch("urllib.request.build_opener") -def test_cloud_create_issue_comment_posts_json( +def test_cloud_create_issue_comment_removed_without_request( mock_build_opener: MagicMock, cloud_env: None, ) -> None: - opener = mock_opener( - mock_build_opener, - { - "id": 501, - "content": {"raw": "Confirmed comment."}, - "user": {"display_name": "Alice"}, - "deleted": False, - }, - ) - - result = cloud.create_issue_comment( - load_config(), - "7", - "Confirmed comment.", - ) + with pytest.raises( + BitbucketError, + match="native issue tracker API was removed by Atlassian on 2026-08-20", + ): + cloud.create_issue_comment( + load_config(), + "7", + "Confirmed comment.", + ) - request = opener.open.call_args.args[0] - assert request.full_url == ("https://api.bitbucket.org/2.0/repositories/apache/magpie/issues/7/comments") - assert request.get_method() == "POST" - assert request.get_header("Content-type") == "application/json" - assert json.loads(request.data.decode("utf-8")) == {"content": {"raw": "Confirmed comment."}} - assert result["issue_id"] == "7" - assert result["comment"]["id"] == 501 + mock_build_opener.assert_not_called() def test_datacenter_create_issue_comment_unsupported( diff --git a/tools/spec-loop/specs/adapters.md b/tools/spec-loop/specs/adapters.md index 03ab4f2bf..431d24d36 100644 --- a/tools/spec-loop/specs/adapters.md +++ b/tools/spec-loop/specs/adapters.md @@ -71,8 +71,8 @@ by swapping the adapter, not the skill. [`vetted-command-surface.md`](vetted-command-surface.md)). - `tools/bitbucket/` — partial Bitbucket Cloud and Bitbucket Data Center bridge foundation. Supports repository metadata reads, - read-only branch restriction context, Cloud-only issue listing/fetching, issue comment fetching, issue attachment metadata fetching, and confirmed issue comment creation, - open pull-request listing, single pull-request fetching, read-only + read-only branch restriction context, open pull-request listing, + single pull-request fetching, read-only pull-request commit fetching, read-only pull-request diff fetching, comments-only pull-request discussion fetching, read-only pull-request review-state fetching, read-only merge-check context fetching, and @@ -86,8 +86,11 @@ by swapping the adapter, not the skill. {merge,squash,rebase} --expected-source-commit `, which re-checks the head commit immediately before the merge POST so a moved head fails closed (#1471); it does not enforce approval or build gates itself. + Atlassian fully removed the native Bitbucket Cloud Issue Tracker API on + 2026-08-20, so the historical `issue` CLI commands now fail closed before + any request and direct callers toward linked Jira coverage. It is not a complete `contract:change-request` or `contract:tracker` - backend yet; deeper Jira handoff, broader issue writes, Data Center + backend yet; deeper Jira handoff, Data Center review/merge writes, broader repository permissions, and fuller Pipelines run/log/retry coverage remain tracked in #606. - `tools/gitlab/` (`magpie-gitlab`) — partial, read-only GitLab REST API v4 @@ -309,14 +312,15 @@ uv run --all-packages --group dev pytest tools/github-rollup/tests commit fetching, read-only pull-request diff fetching, comments-only pull-request discussion fetching, read-only review-state fetching, Cloud-only pull-request task listing/fetching, Cloud-only asynchronous merge task-status fetching, read-only merge-check context fetching, - read-only pull-request status fetching, and Cloud-only issue listing/fetching, issue comment fetching, - issue attachment metadata fetching, and confirmed issue comment creation; - #606 remains open for full tracker/change-request coverage. + and read-only pull-request status fetching. Native Bitbucket Cloud issue + commands fail closed because Atlassian removed the Issue Tracker API on + 2026-08-20; #606 remains open for linked Jira tracker coverage and the + remaining change-request gaps. - Bitbucket write operations follow the framework write-path discipline: the calling skill must obtain explicit user confirmation before invoking a mutation. The bridge executes only the confirmed action; current write - coverage is Bitbucket Cloud issue-comment creation, Bitbucket Cloud - pull-request comment creation, backend-neutral `post_review` through + coverage is Bitbucket Cloud pull-request comment creation, + backend-neutral `post_review` through comment/approve/request-changes, backend-neutral `reject`, and Bitbucket Cloud pull-request approve/unapprove, request-changes/remove-request-changes, decline, and