{
"meta": {
"agent": "planner",
"task_id": "457",
"title": "AW-11: Create BullMQJobQueue implementing JobQueue interface",
"created_at": "2026-04-13T12:00:00Z"
},
"inputs": [
{
"name": "issue-457",
"type": "link",
"ref": "https://github.com/AgentSDE/agent-core/issues/457",
"notes": "AW-11 issue: BullMQ job queue implementation"
},
{
"name": "job-queue-interface",
"type": "file",
"ref": "src/queue/job-queue.interface.ts",
"notes": "JobQueue interface: enqueue() + onJob()"
},
{
"name": "sqlite-job-queue",
"type": "file",
"ref": "src/queue/sqlite-job-queue.ts",
"notes": "Reference implementation with per-issue dedup and stale recovery"
},
{
"name": "queue-module",
"type": "file",
"ref": "src/queue/queue.module.ts",
"notes": "Current module: SqliteJobQueue + JOB_QUEUE token"
},
{
"name": "invoke-module",
"type": "file",
"ref": "src/invoke/invoke.module.ts",
"notes": "Existing BullModule.forRootAsync pattern for Redis connection"
},
{
"name": "config-schema",
"type": "file",
"ref": "src/config/config.schema.ts",
"notes": "Joi validation schema — add JOB_QUEUE_DRIVER here"
}
],
"outputs": [
{
"name": "plan",
"type": "spec",
"format": "md",
"content": "plan.md"
}
],
"files": [
{
"path": "src/queue/bullmq-job-queue.ts",
"action": "create",
"reason": "BullMQ implementation of JobQueue interface"
},
{
"path": "src/queue/bullmq-job-queue.spec.ts",
"action": "create",
"reason": "Unit tests for BullMQJobQueue"
},
{
"path": "src/queue/queue.module.ts",
"action": "modify",
"reason": "Conditional provider: BullMQ vs SQLite based on JOB_QUEUE_DRIVER"
},
{
"path": "src/config/config.schema.ts",
"action": "modify",
"reason": "Add JOB_QUEUE_DRIVER validation"
}
],
"steps": [
{
"id": "S1",
"summary": "Add JOB_QUEUE_DRIVER to config schema and create BullMQJobQueue implementation",
"acceptance": [
"JOB_QUEUE_DRIVER added to config.schema.ts with valid values 'sqlite'|'bullmq', default 'sqlite'",
"BullMQJobQueue class implements JobQueue interface (enqueue + onJob)",
"Uses BullMQ Queue for enqueuing, Worker for consuming",
"Redis connection uses REDIS_HOST/REDIS_PORT/REDIS_DB from ConfigService",
"Per-issue concurrency control prevents parallel processing of same issue",
"Stalled jobs auto-retried by BullMQ (no manual recovery in onModuleInit)",
"Invalid JOB_QUEUE_DRIVER value rejected by Joi at startup"
],
"depends_on": []
},
{
"id": "S2",
"summary": "Update QueueModule for conditional driver registration and add unit tests",
"acceptance": [
"QueueModule reads JOB_QUEUE_DRIVER from ConfigService",
"JOB_QUEUE token resolves to BullMQJobQueue when driver=bullmq",
"JOB_QUEUE token resolves to SqliteJobQueue when driver=sqlite (default)",
"BullModule imported only when driver=bullmq",
"Unit tests cover: enqueue, onJob handler, per-issue dedup, error handling, cleanup",
"npm run build passes",
"npm run test passes",
"npm run lint passes with zero warnings"
],
"depends_on": [
"S1"
]
}
],
"risks": [
{
"risk": "BullModule.forRootAsync registered in both InvokeModule and QueueModule may conflict",
"mitigation": "NestJS deduplicates global BullModule config; if conflict, hoist to AppModule as AW-3 follow-up"
},
{
"risk": "Per-issue dedup harder in BullMQ than SQLite atomic queries",
"mitigation": "Use BullMQ concurrency=1 with job grouping by issueNumber, or a Redis-based lock"
}
],
"assumptions": [
"Redis is available and running (AW-1 established Redis as a hard dependency)",
"BullMQ dependencies already installed from AW-2 (bullmq, ioredis, @nestjs/bullmq)",
"No migration of in-flight SQLite jobs needed — clean-slate switch on restart is acceptable per issue spec"
],
"open_questions": []
}