Bitbucket Cloud Forge integration

Bitbucket Cloud has a native Forge app, provider adapter and guarded activation workflow. Registration and installed-app acceptance remain unverified; contract tests do not establish a working external integration. The authenticated catalog remote is implemented at POST /api/bitbucket/forge/catalog.

Register an OpenReview Forge app in Atlassian's commercial cloud and configure the web server's BITBUCKET_FORGE_APP_ID and BITBUCKET_FORGE_ENVIRONMENT_ID with its exact registered ARIs. Its remote endpoint needs auth.appSystemToken.enabled: true and the native read:repository:bitbucket scope. Forge sends the invocation JWT in Authorization and the short-lived app token in x-forge-oauth-system. Neither token belongs in a URL or log.

POST /api/bitbucket/forge/connect invokes the native proof before installation binding. A verified FIT must have a user principal. The user token's /2.0/user identity must be that exact active human account; the system token must identify a distinct active app_user publisher. A complete, filtered /2.0/workspaces/{workspace}/permissions read must return that human as the signed workspace's native owner. Foreign rows, missing identities/status, duplicates, incomplete pages, changed filters and redirects fail closed. A Forge principal or a workspace-settings page alone does not grant admin access. This proof requires auth.appUserToken.enabled: true, read:user:bitbucket and read:workspace:bitbucket in addition to the repository scopes. Native system-account and permission responses must be verified on an installed app.

Once Forge registration is configured, an OpenReview workspace admin can generate a five-minute authorization in Settings → Code providers. Only its SHA-256 hash is stored. The native Forge settings UI must send the copied code as the sole JSON body field, {"code":"<64 hex characters>"}, alongside Forge's signed authorization and user/system token headers. The route limits the streamed body to 1 KiB, rechecks the approving admin's current access, verifies native ownership and publisher identity, encrypts only the system token with workspace/provider authenticated scope, and atomically consumes both authorization and invocation receipt. It rejects cross-workspace installation transfers and permits same-workspace reconnection with fresh authorization. It stores an explicit one-hour credential expiry, below the documented two-hour minimum, without decoding opaque OAuth claims. The user token is never persisted or returned. The public response reports suspension until native event delivery, scheduled renewal and explicit administrator activation; no repository selection or manual webhook instructions are offered for that suspended connection. Admins can revoke an unused authorization and disconnect a connection. The native Forge settings UI is implemented in forge/bitbucket; app registration, deployment and installed-app acceptance remain required.

The endpoint verifies RS256 signatures against Atlassian's fixed public JWKS, issuer, audience, expiry, app, environment and installation workspace. The caller cannot choose a workspace in its body. Repository discovery uses that signed workspace UUID, rejects redirects or pagination outside its API path, checks returned repository/workspace identities, and returns native IDs and organization provenance. Empty repositories retain a null default branch. Discovery fails rather than certifying coverage after 100 pages or responses larger than 4 MB. No credentials or installations are persisted by this read-only endpoint.

src/bitbucket-cloud.ts now implements native branch/source enumeration and PR ingestion. It addresses repositories by workspace/repository UUID, verifies native identities and full names, walks immediate directories without following untrusted links, and checks each source entry's pinned revision. Its per-file cache version is the repository revision (stored alongside the path), not a fabricated blob hash. Source reads validate metadata/byte size and exclude binary, symlink and subrepository content. LFS redirects are rejected. Traversal is limited to 10,000 entries and 100 pages per listing; failures are explicit. Branch protection has not been fetched and must not be inferred from this read adapter.

PR reads request a pinned source/destination commit diff directly, avoiding the redirecting PR diff endpoints. The adapter verifies native diff statistics against parsed paths, hunk lengths and added/removed line counts, retrieves the merge base, and rechecks current revisions/state before returning. Binary changes, quoted paths or metadata-only changes that the shared parser cannot fully represent fail visibly rather than yielding a complete review. Fork source retrieval and all live API behavior remain unverified. The shared provider factory now wires this adapter for authorized discovery, selection and indexing. Connections remain suspended until guarded activation succeeds; this does not establish live native acceptance.

The adapter also implements summary/inline comment publication and native parent-thread replies using the shared review/comment formatting. Publication requires the app's independently verified publisher UUID, checks returned app_user authors and exact content/anchors, and reuses only that publisher's matching root comments with exact run/revision markers. Findings must belong to the current head and added diff lines. Revision checks surround writes; Bitbucket's comment endpoint does not provide an atomic expected-revision precondition, so a change during publication can leave a comment at the older recorded revision while the operation fails visibly and stops further publication. No merge action is implemented.

Follow-up authorization queries native effective repository permissions, scoped to the repository and author UUID, and requires active human write/admin access. Missing native identity/account status, incomplete enumeration or inaccessible permissions fail closed. Permission reads require native repository admin access; the Forge app's ability to retrieve these fields/permissions must be established through live acceptance. Replies validate the parent in the current PR and reuse only app-authored markers in the same thread. These methods satisfy the shared provider interface, but do not establish a working installed integration.

Remaining work: register/install the native app and verify its settings UI in an authorized workspace; deploy and verify the direct hourly renewal trigger and lifecycle events; connect selected repositories; verify opt-in native approval gates; verify the event/review/follow-up pipeline on the installed app; verify native permission/account fields, close unsupported diff coverage and verify forks and the complete native acceptance matrix. Native approval gates are implemented with fixture/durable tests; installed Forge approval acceptance remains required. No live Forge installation has been tested.

References: Forge remote essentials, calling APIs from a remote, Bitbucket repository REST API.

Read contracts: native source API, native commit comparison and merge base, native PR API.

Direct scheduled token renewal

POST /api/bitbucket/forge/refresh accepts only the registered app/environment FIT with signed app.module.type: core:endpoint, key openreview-token-refresh, and no user principal. Installation/workspace binding must already exist and remain active. It verifies the newly delivered token against the native active app_user and the stored publisher UUID, rejects older invocation timestamps, and atomically replaces encrypted credentials only if the previous encrypted value is unchanged. Invocation receipts prevent replay. Disconnects fail closed, and token renewal never clears suspension or enables reviews. Tokens remain opaque and are not logged or returned.

Configure the native app's direct remote scheduled trigger with an hourly interval and endpoint key openreview-token-refresh, route /api/bitbucket/forge/refresh, and auth.appSystemToken.enabled: true. Request read:app-system-token and the relevant native Bitbucket scopes, including read:user:bitbucket for publisher verification. Do not route the trigger through a Forge function: its shorter token lifetime does not establish the direct trigger's minimum two-hour guarantee. The remote retains a conservative 90-minute expiry; live hourly deliveries and missed-delivery/expiry recovery still require installed-app acceptance. This endpoint alone does not establish native refresh availability.

See direct remote scheduled triggers and signed module identity.

The provider factory decrypts credentials using the workspace/provider scope, validates token formatting and native publisher identity metadata, and rejects expired, future-dated or overlong-lived credential records before making native requests. Discovery remains pinned to the stored native workspace UUID; selected repositories retain native UUIDs. An empty native repository was indexed through the shared durable job pipeline in a fixture contract test. It does not prove live Forge indexing or review publication.

Native event implementation must use Forge's avi:bitbucket:* payloads rather than Bitbucket webhook shapes. PR-comment creation events identify the comment, so follow-ups must fetch actual native content and author before authorization. Forge's documented removal notification uses a preUninstall module, which supports a function or direct remote endpoint; an ordinary fabricated avi:forge:uninstalled:app event is not a supported substitute. See Bitbucket Forge events and app lifecycle events.

Signed product event dispatch

POST /api/bitbucket/forge/events accepts the registered FIT only from signed core:endpoint module key openreview-events, with no user principal. Configure a direct remote trigger for native repository push, PR created/updated/fulfilled/rejected and PR-comment created events; request read:repository:bitbucket and read:pullrequest:bitbucket. The streamed body is bounded to 400 KB, validated against native Forge fields, and bound to the FIT workspace. The atomic backend mutation rechecks the active installation, consumes the invocation receipt and deduplicates normalized native payloads (including event timestamp) across fresh FIT retries before scheduling the existing durable event worker. This is delivery deduplication, not a substitute for revision/run idempotency in review publication. Suspended installations consume the signed invocation and record delivery time only; they do not retain payloads or schedule jobs. An administrator must activate after native delivery and renewal.

The worker selects only repositories bound to that connection/native UUID. Default-branch pushes schedule indexing; PR events fetch the current native change and apply effective automatic/draft/re-review/branch policies before enqueueing. Closed changes use current native state. Comment events retrieve native content and author, reject app-generated/foreign/deleted/pending comments, require current human write/admin permissions, and resolve the actual thread root. Missing comments or parents retry visibly rather than completing silently. Review commands enqueue the current revision; other mentions schedule the existing grounded conversation workflow. No model calls or provider writes occurred during these fixture checks. The native manifest wires these triggers and pre-uninstall removal; installed-app acceptance remains unfinished.

Native settings and uninstall

The native app source and lockfile live in forge/bitbucket. Its workspace settings Custom UI sends only an explicit connection code through Forge bridge to the existing native ownership-verification route. Native input validation, fixed remote path/body, single submission guard, suspension messaging, generic failure recovery and post-submission code clearing are implemented; no token/code is persisted by the UI. Static local browser preview proves only rendering and invalid-input behavior, not an installed Forge session. The manifest uses actual operator-provided app ID/origin environment variables and declares native event subscriptions, direct hourly renewal and direct pre-uninstall endpoint. Native read-PR scope permits comment publication. The manifest now additionally requests write-PR scope for opt-in approval and withdrawal; the platform scope is broader than those operations, so native installation/upgrade must approve it. OpenReview never calls merge APIs. Repository admin scope supports effective permission reads and also permits configuration changes at the platform level; those mutations are not used by OpenReview.

POST /api/bitbucket/forge/uninstall requires the registered signed core:endpoint module key openreview-uninstall, no principal, and the FIT installation/workspace. It ignores body-selected identities. The atomic service-only mutation consumes the receipt, rejects tenant mismatch/replay/expiry, clears stored credentials, disables connected repositories and cancels running/pending indexing using the shared revocation helper. Unknown/already disconnected installations are idempotent with a fresh native invocation. Uninstall is non-blocking in Forge, so missed notifications cannot guarantee immediate local cleanup; explicit admin disconnect and credential expiry remain recovery paths. Native removal delivery must be tested before claiming revocation acceptance. See native pre-uninstall endpoint support.

Administrator activation

Settings → Code providers displays native delivery and scheduled renewal timestamps. After both arrive, Verify access and activate calls expiry-checked, workspace-bound native repository discovery before clearing suspension. Backend authorization is checked before the API call and again atomically at activation; both timestamps must remain recent (24 hours for events, 90 minutes for renewal), and encrypted credentials must match those checked. Reconnection clears both proofs. Disconnect, uninstall, credential changes, stale proofs, foreign/viewer access or failed native discovery block activation. Repository selection remains separate and automatic reviews use effective repository policies. Setup events are not replayed as reviews after activation. Native acceptance must still cover the full delivery and review lifecycle.

Exact-revision runtime snapshot

The runtime adapter uses the documented native source API with the installation's scoped Forge system token and immutable workspace/repository UUIDs. It verifies the repository and full 40-character commit before and after retrieval. An unfiltered, paginated native tree is traversed at that commit. Every regular file is read as raw bytes; binaries, empty files and executable attributes are preserved. Review/index redaction and text-size filters are not used. Metadata must match path, commit, type, declared size and known attributes, and actual bytes must match the native size.

The shared regular-file USTAR writer creates one source/ wrapper for the existing separate worker. Source is bounded to 20 MB uncompressed and 10,000 native entries; unsupported USTAR paths fail. Links and subrepositories are currently unsupported and fail visibly. Bitbucket LFS returns a native redirect to its media platform; redirects are rejected rather than packaging the pointer or forwarding Forge credentials to another host. Native access failures, incomplete/foreign/repeated pagination, changed repository/commit, unknown/missing metadata and oversized files fail source preparation. Runtime remains disabled by default and execution only occurs in an approved no-network worker.

Two native-response regressions and one durable Convex regression cover raw file fidelity, modes, source failure cases and the authorized worker snapshot endpoint. These are contracts, not a live Forge installation or Docker execution. A registered/deployed/installed Forge App and native first-revision runtime verification remain necessary for live acceptance.

Personal author proof foundation

The local Forge adapter now provides API-backed personal verification and revalidation. Given an already verified, current FIT user principal and the selected immutable workspace/repository IDs, it fetches an active human /2.0/user, reads the exact native Git repository with that user token, checks its UUID/workspace/name, and reads the user again to reject identity changes during verification. No workspace-owner permission is needed for this read-only personal proof. Stored credential parsing is strict and bounds the intended encrypted grant to one hour; expired, future-dated, substituted installation/account and invalid records fail closed. OAuth bytes remain opaque. The one-hour bound is below the documented approximately two-hour minimum remaining native lifetime, with fresh API calls handling earlier revocation. See native token lifetime and opaque OAuth guidance.

PR ingestion now returns the immutable authorExternalId only when the native author explicitly has human type: user, and checks author UUID/type for changes across ingestion. Missing or app account types do not authorize personal context. Native-response regressions exercise principal/account/repository substitution, missing status, inactive/app accounts, denial, expiry, replacement and ingestion author races. This is a foundation, not a completed personal setup workflow: durable single-use authorization, user-facing Forge confirmation and repository-context consumption still need integration. Bitbucket remains excluded from supported personal identity providers until that integration is complete. These local changes have not been deployed, and no live Forge acceptance is claimed.

Personal confirmation and authorized context

Supersedes the foundation-only note above: the personal workflow is now integrated. Settings → Personal setup offers each active Bitbucket connection a five-minute, single-use code. Only its SHA-256 hash is persisted with the approving user, selected repository, active connection and credential digest. The native bitbucket:workspacePersonalSettingsPage uses the existing read-user remote and needs no workspace-owner role. Paste the code while signed in as the PR author and explicitly click Link personal identity. The fixed remote POST /api/bitbucket/forge/identity verifies FIT signature/app/environment/installation/workspace and user principal, validates a strict 1 KiB code body, performs native personal/repository reads, then encrypts the opaque user token in the organization/connection/user scope. The backend atomically rechecks current repository access and installation authority, rejects duplicate account claims, and consumes both the code and signed invocation receipt. Scheduled expiry clears saved user credentials; manual disconnect also invalidates the identity generation. PAT linking remains rejected for Bitbucket Cloud.

The link expires after at most one hour, with native API verification detecting earlier revocation. There is no automatic offline personal-token refresh claim: after expiry or installation credential renewal, generate a new code and confirm again. See native personal settings module. Existing workspace-owner connection remains a separate flow. The native manifest adds a personal page without additional scopes.

Cross-repository context resolves the actual human PR UUID through this connection-specific link. Every Bitbucket source additionally requires the same OpenReview user’s current personal link for its connection, and pins that link’s ID/generation/native UUID in the context receipt. Missing links omit the source with a coverage explanation. Personal API checks verify primary/source repository read access before any bot source retrieval, after retrieval, and at the shared pre-publication native validation point. Changed credentials, expired/revoked/replaced identities, foreign installations, lost user/repository access and source revision/cluster changes invalidate context, including cached results.

Signed remote, native bridge invocation, durable authorizations and actual provider-factory retrieval regressions pass, including no bot source reads after personal-source denial, replay, account collisions, viewer restrictions, expiry erasure and changed installation credentials. Local computer use verified the built personal page and native invalid-input rejection only; this is not an installed Forge session. Registration/deployment/installation and native personal confirmation, expiry/revocation and full review/follow-up acceptance remain required.

Opt-in native approval

The Bitbucket adapter implements native approval/withdrawal, exact-head status checks, native mergeability requirements, human change-request guards, timestamp-bound bot receipts and durable event rechecks. This is implemented software with native-response and coordinator checks, not live installed-app acceptance. See approval behavior and limits. The Forge manifest adds write:pullrequest:bitbucket for these endpoints and the documented reviewer-status event. Automatic approval remains off until a trusted admin enables it; deploying or upgrading an app does not enable an OpenReview policy.