AI Agents SDE Task Viewer
      • Context
      • Plan
      • Prd
  1. Home
  2. AgentSDE
  3. agent-core
  4. gh-440
  5. plan
  6. context.json
context.json(4.9 KB)· Apr 12, 2026
{
  "meta": {
    "agent": "planner",
    "task_id": "440",
    "title": "AW-4: Refactor ClaudeInvocationService — enqueue to Redis instead of spawning",
    "created_at": "2026-04-12T12:00:00Z"
  },
  "inputs": [
    {
      "name": "issue-440",
      "type": "context",
      "ref": "https://github.com/AgentSDE/agent-core/issues/440",
      "notes": "Full AW-4 spec with acceptance criteria and edge cases"
    },
    {
      "name": "claude-invocation.service.ts",
      "type": "file",
      "ref": "src/invoke/claude-invocation.service.ts",
      "notes": "Current spawn-based invocation — 427 lines, all spawn logic in invoke()"
    },
    {
      "name": "invoke.module.ts",
      "type": "file",
      "ref": "src/invoke/invoke.module.ts",
      "notes": "Current module — imports EventModule, provides SignalParser"
    },
    {
      "name": "signal-result.ts",
      "type": "file",
      "ref": "src/invoke/signal-result.ts",
      "notes": "SignalResult interface — must remain unchanged"
    },
    {
      "name": "claude-cli.provider.ts",
      "type": "file",
      "ref": "src/llm/claude/claude-cli.provider.ts",
      "notes": "Caller — must not change; calls claude.invoke(skill, env, taskId)"
    },
    {
      "name": "AGENTS.md",
      "type": "context",
      "ref": "AGENTS.md",
      "notes": "Codebase conventions — Wave 2+ stub guidance, testing patterns"
    }
  ],
  "outputs": [
    {
      "name": "plan",
      "type": "spec",
      "format": "md",
      "content": "plan.md"
    }
  ],
  "files": [
    {
      "path": "src/invoke/claude-invocation.service.ts",
      "action": "modify",
      "reason": "Replace spawn with queue.add + waitForResult + file-based signal parsing"
    },
    {
      "path": "src/invoke/invocation-result.listener.ts",
      "action": "create",
      "reason": "BullMQ processor for phase-result queue; resolves pending promises by jobId"
    },
    {
      "path": "src/invoke/invoke.module.ts",
      "action": "modify",
      "reason": "Register BullMQ queues and InvocationResultListener"
    },
    {
      "path": "src/invoke/claude-invocation.service.spec.ts",
      "action": "modify",
      "reason": "Replace spawn mocks with BullMQ queue mocks"
    }
  ],
  "steps": [
    {
      "id": "S1",
      "summary": "Create InvocationResultListener — BullMQ processor for phase-result queue with waitForResult(jobId, timeout) promise map",
      "acceptance": [
        "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"
      ],
      "depends_on": []
    },
    {
      "id": "S2",
      "summary": "Refactor ClaudeInvocationService.invoke() — replace spawn with queue enqueue + await result + file-based signal parsing",
      "acceptance": [
        "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"
      ],
      "depends_on": [
        "S1"
      ]
    },
    {
      "id": "S3",
      "summary": "Update InvokeModule — register BullMQ queues (phase-invoke, phase-result) and InvocationResultListener provider",
      "acceptance": [
        "InvokeModule imports BullModule.registerQueue for phase-invoke and phase-result",
        "InvocationResultListener is registered as a provider",
        "npm run build passes"
      ],
      "depends_on": [
        "S1",
        "S2"
      ]
    },
    {
      "id": "S4",
      "summary": "Update tests — replace spawn mocks with BullMQ queue mocks, add timeout and missing-file edge case tests",
      "acceptance": [
        "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"
      ],
      "depends_on": [
        "S2",
        "S3"
      ]
    }
  ],
  "risks": [
    {
      "risk": "AW-3 (#439) not merged — BullModule import requires @nestjs/bullmq and Redis",
      "mitigation": "Branch from AW-3 or use minimal inline BullMQ stub per AGENTS.md Wave 2+ guidance"
    },
    {
      "risk": "Race between worker writing ai-output.jsonl and result enqueue",
      "mitigation": "Worker must fsync before enqueuing result; document this contract"
    }
  ],
  "assumptions": [
    "AW-3 (#439) provides @nestjs/bullmq registration with Redis connection in AppModule or a shared BullMQ config module",
    "The worker (separate AW task) writes ai-output.jsonl as newline-delimited JSON with the same stream-json format",
    "formatStreamEvent() can be removed or kept as dead code — worker handles real-time logging"
  ],
  "open_questions": [
    "Should formatStreamEvent() be preserved as a utility for log replay, or removed entirely?"
  ]
}
Plan