{
"meta": {
"agent": "planner",
"task_id": "548",
"title": "BJ-8: BitbucketService (GitHub parity surface)",
"created_at": "2026-04-23T17:11:46Z"
},
"inputs": [
{
"name": "issue",
"type": "link",
"ref": "https://github.com/AgentSDE/agent-core/issues/548",
"notes": "Refined PRD with acceptance criteria + scope"
},
{
"name": "github.service.ts",
"type": "file",
"ref": "src/github/github.service.ts",
"notes": "Reference implementation; mirror public method signatures"
},
{
"name": "github.module.ts",
"type": "file",
"ref": "src/github/github.module.ts",
"notes": "Factory pattern for PLATFORM_AUTH_PROVIDER to mirror"
},
{
"name": "platform-auth-provider.interface.ts",
"type": "file",
"ref": "src/platform/platform-auth-provider.interface.ts",
"notes": "Contract — PlatformAuthProvider.getToken()"
},
{
"name": "github.service.spec.ts",
"type": "file",
"ref": "src/github/github.service.spec.ts",
"notes": "Test pattern — fetch spy + mock auth provider"
}
],
"outputs": [
{
"name": "plan.md",
"type": "spec",
"format": "md",
"content": "Implementation plan for BitbucketService and module wiring"
},
{
"name": "BitbucketService",
"type": "spec",
"format": "text",
"content": "NestJS injectable; baseUrl https://api.bitbucket.org/2.0; 18 public methods; private request<T>() with 429/Retry-After + 401 refresh-once"
},
{
"name": "BitbucketModule",
"type": "spec",
"format": "text",
"content": "Exports BitbucketService + PLATFORM_AUTH_PROVIDER token via factory injecting BitbucketOAuthAuthProvider"
}
],
"files": [
{
"path": "src/bitbucket/bitbucket.service.ts",
"action": "create",
"reason": "Primary deliverable — Bitbucket API client"
},
{
"path": "src/bitbucket/bitbucket.module.ts",
"action": "create",
"reason": "DI wiring + auth provider factory"
},
{
"path": "src/bitbucket/bitbucket.service.spec.ts",
"action": "create",
"reason": "Unit tests for every public method"
},
{
"path": "src/bitbucket/index.ts",
"action": "create",
"reason": "Barrel export, mirrors src/github/index.ts"
}
],
"steps": [
{
"id": "S1",
"summary": "Create BitbucketService with all 18 public methods + private request/sleep helpers, base https://api.bitbucket.org/2.0, treat owner arg as workspace slug",
"acceptance": [
"src/bitbucket/bitbucket.service.ts exists and compiles under strict TS",
"All 18 methods from issue acceptance criteria are implemented as public async methods",
"Private request() uses Bearer token from authProvider.getToken(), retries once on 429 honouring Retry-After, refreshes once on 401",
"enableAutoMerge throws NotImplementedException; updatePRBranch logs warn and returns",
"addLabel/removeLabel implement title-prefix fallback (PUT pullrequest with mutated title)"
],
"depends_on": []
},
{
"id": "S2",
"summary": "Create BitbucketModule with factory provider for PLATFORM_AUTH_PROVIDER injecting BitbucketOAuthAuthProvider, plus index.ts barrel",
"acceptance": [
"src/bitbucket/bitbucket.module.ts exists, imports ConfigModule, exports BitbucketService and PLATFORM_AUTH_PROVIDER",
"Factory uses ConfigService and returns new BitbucketOAuthAuthProvider(config) (parallel to github.module.ts)",
"src/bitbucket/index.ts re-exports BitbucketModule and BitbucketService"
],
"depends_on": [
"S1"
]
},
{
"id": "S3",
"summary": "Write bitbucket.service.spec.ts covering every public method plus 429-retry, 401-refresh, label round-trip, and enableAutoMerge throw",
"acceptance": [
"Spec file mocks PlatformAuthProvider and globalThis.fetch (mirrors github.service.spec.ts)",
"Each public method has at least one URL + method + body assertion",
"429 retry test asserts second fetch fires after Retry-After delay",
"401 refresh test asserts getToken called twice and request retried once",
"npm run test and npm run lint pass with zero warnings"
],
"depends_on": [
"S1",
"S2"
]
}
],
"risks": [
{
"risk": "BitbucketOAuthAuthProvider (#542) not yet merged on rc/atlassian-integration",
"mitigation": "Import the path declared by #542 (src/atlassian/bitbucket-oauth-auth.provider); if file is missing at implementation time, signal BLOCKED rather than stub it"
},
{
"risk": "Bitbucket API shape differs from GitHub (multipart src upload, state enum names, no native auto-merge)",
"mitigation": "Document divergences inline; honour explicit issue assumptions (NotImplementedException for auto-merge, title-prefix labels)"
}
],
"assumptions": [
"owner argument across the public surface is the Bitbucket workspace slug (preserves GitHubService signature for substitutability)",
"401 retry policy is fail-once: refresh token once and retry; second 401 throws",
"Squash is the default merge strategy (matches GitHub enableAutoMerge mutation)"
],
"open_questions": []
}