AI Agents SDE Task Viewer
      • Context
      • Plan
      • Prd
  1. Home
  2. AgentSDE
  3. agent-core
  4. gh-546
  5. plan
  6. plan.md
plan.md(4.3 KB)· Apr 23, 2026· 3 min read
  • Summary
  • Files
  • Steps
  • Verification
  • Risks
  • Open Questions

BJ-6: BitbucketAdapter + webhook route#

Summary#

Add a BitbucketAdapter (mirroring GitHubAdapter) that normalises 7 Bitbucket webhook events into DispatchEvents, plus a POST /webhooks/bitbucket route. Secret verification uses URL-embedded ?secret= and an IP allowlist (Bitbucket raw webhooks have no HMAC).

Files#

PathActionPurpose
src/webhook/adapters/bitbucket.adapter.tscreatePlatformAdapter for Bitbucket: verifySignature() + normalize() for 7 event types
src/webhook/adapters/bitbucket.adapter.spec.tscreateUnit tests per event type, secret + IP allowlist paths, bot self-filter
src/webhook/adapters/platform-adapter.interface.tsmodifyExtend verifySignature with optional context?: { clientIp?: string; query?: Record<string,string> } so adapters can inspect non-header auth
src/webhook/adapters/github.adapter.tsmodifyAccept (and ignore) the new optional context arg to preserve interface parity
src/webhook/webhook.controller.tsmodifyAdd @Post('bitbucket') handler; pass req.ip + req.query to verifySignature; use tenantResolver.resolveFromWebhook('bitbucket', …)
src/webhook/webhook.module.tsmodifyProvide BitbucketAdapter and register it in onModuleInit() next to GitHubAdapter
src/webhook/dto/dispatch-event.dto.tsmodifyExtend DispatchEventType with 'pr_updated' | 'approved' | 'changes_requested' | 'push' (new types required by the mapping)
src/config/config.schema.tsmodifyAdd optional BITBUCKET_WEBHOOK_SECRET (required when route used) and optional BITBUCKET_WEBHOOK_IP_ALLOWLIST (comma-separated CIDRs/IPs)
.env.examplemodifyDocument the two new env vars
CLAUDE.mdmodifyAdd BITBUCKET_WEBHOOK_SECRET + BITBUCKET_WEBHOOK_IP_ALLOWLIST to the env table (AGENTS.md rule)

Steps#

  1. Extend PlatformAdapter.verifySignature with an optional context arg; update GitHubAdapter to match the new shape (no behavior change).
  2. Extend DispatchEventType union with the four new values.
  3. Implement BitbucketAdapter — platform = 'bitbucket'; constructor injects ConfigService + PLATFORM_AUTH_PROVIDER (for bot self-filter); cache botUsername at construction.
  4. verifySignature: timingSafeEqual the context.query.secret against BITBUCKET_WEBHOOK_SECRET; if BITBUCKET_WEBHOOK_IP_ALLOWLIST is set, reject context.clientIp outside the list; throw UnauthorizedException on failure. Add a block comment noting Bitbucket has no HMAC.
  5. normalize: switch on x-event-key header and map each of the 7 events to a DispatchEvent. Extract issueNumber from PR branch / title / body via a helper that mirrors GitHubAdapter.extractIssueNumber.
  6. For pullrequest:comment_created, skip bot-authored comments, require /agentsde (or legacy /agent) via the same directive regex.
  7. Wire controller: add @Post('bitbucket'), build context = { clientIp: req.ip, query: req.query as Record<string,string> }, call adapter, persist WebhookDeliveryEntity with source: 'bitbucket', resolve tenant with 'bitbucket'.
  8. Register BitbucketAdapter in WebhookModule providers and onModuleInit().
  9. Add config schema entries and .env.example lines; update CLAUDE.md env table.
  10. Write unit tests: valid/invalid secret, IP allow/deny, all 7 event keys, unknown x-event-key → { event: null }, bot self-filter, /agentsde directive on PR comment.

Verification#

  • npm run test — all unit tests pass, BitbucketAdapter spec green.
  • npm run lint — zero warnings.
  • npm run build — clean (confirms new DispatchEventType values compile across dispatch/phase consumers).

Risks#

  • Interface change ripples: extending verifySignature signature touches every adapter. Mitigated by making context optional so GitHubAdapter is unaffected.
  • New DispatchEventType values: downstream switch statements (dispatch.service, phase-router) may not handle them yet — acceptable for BJ-6 (Wave 2 handler wiring lands in later BJ-* tickets), but the build must still pass.

Open Questions#

  • Should BITBUCKET_WEBHOOK_SECRET be required in config.schema when absent, or remain optional (route returns 401 if the env var is unset)? Assumption: optional — matches the lazy-enablement pattern used by GitHub App vars.
ContextPrd