{
"issueNumber": 440,
"branchName": "feat/440-refactor-invoke-bullmq",
"generatedAt": "2026-04-12T12:00:00Z",
"stories": [
{
"id": "S1",
"title": "Create InvocationResultListener — BullMQ processor for phase-result queue with waitForResult promise map",
"priority": 1,
"dependsOn": [],
"acceptanceCriteria": [
"InvocationResultListener is a @Processor('phase-result') class",
"waitForResult(jobId, timeoutMs) returns a promise that resolves with worker result payload",
"Unmatched jobIds log a warning and do not throw",
"Timeout rejects with a descriptive error"
],
"passes": false,
"completedAt": null
},
{
"id": "S2",
"title": "Refactor ClaudeInvocationService.invoke() — replace spawn with queue enqueue + await result + file-based signal parsing",
"priority": 2,
"dependsOn": [
"S1"
],
"acceptanceCriteria": [
"invoke() no longer imports or calls child_process.spawn",
"invoke() enqueues a job to phase-invoke BullMQ queue with all required payload fields",
"invoke() awaits result from InvocationResultListener.waitForResult(jobId, CLAUDE_TIMEOUT_SECS + 100s)",
"Signal parsing reads from {TASK_DIR}/meta/ai-output.jsonl",
"Returns identical SignalResult interface to callers",
"Missing ai-output.jsonl returns empty string for signal detection"
],
"passes": false,
"completedAt": null
},
{
"id": "S3",
"title": "Update InvokeModule — register BullMQ queues and InvocationResultListener provider",
"priority": 3,
"dependsOn": [
"S1",
"S2"
],
"acceptanceCriteria": [
"InvokeModule imports BullModule.registerQueue for phase-invoke and phase-result",
"InvocationResultListener is registered as a provider",
"npm run build passes"
],
"passes": false,
"completedAt": null
},
{
"id": "S4",
"title": "Update tests — replace spawn mocks with BullMQ queue mocks, add edge case coverage",
"priority": 4,
"dependsOn": [
"S2",
"S3"
],
"acceptanceCriteria": [
"All existing signal/mapping tests pass with mocked queues",
"New test: timeout propagation from waitForResult",
"New test: missing ai-output.jsonl returns blocked/transient",
"npm run test passes",
"npm run lint passes"
],
"passes": false,
"completedAt": null
}
]
}