AI Agents SDE Task Viewer
      • Context
      • Plan
      • Prd
  1. Home
  2. AgentSDE
  3. agent-core
  4. gh-441
  5. plan
  6. context.json
context.json(4.0 KB)ยท Apr 12, 2026
{
  "meta": {
    "agent": "planner",
    "task_id": "441",
    "title": "AW-5: Consume phase-result queue and handle crash recovery",
    "created_at": "2026-04-12T12:00:00Z"
  },
  "inputs": [
    {
      "name": "issue-441",
      "type": "link",
      "ref": "https://github.com/AgentSDE/agent-core/issues/441",
      "notes": "Crash recovery for phase results after restart"
    },
    {
      "name": "invocation-result-listener",
      "type": "file",
      "ref": "src/invoke/invocation-result.listener.ts",
      "notes": "Created by AW-4 (#440); target for recoverResult()"
    },
    {
      "name": "sqlite-job-queue",
      "type": "file",
      "ref": "src/queue/sqlite-job-queue.ts",
      "notes": "onModuleInit() stale job detection"
    },
    {
      "name": "claude-invocation-service",
      "type": "file",
      "ref": "src/invoke/claude-invocation.service.ts",
      "notes": "waitForResult() timeout addition"
    }
  ],
  "outputs": [
    {
      "name": "plan",
      "type": "plan",
      "format": "md",
      "content": "plan.md"
    }
  ],
  "files": [
    {
      "path": "src/invoke/invocation-result.listener.ts",
      "action": "modify",
      "reason": "Add recoverResult() with TaskStateService + InternalAdapterService injection"
    },
    {
      "path": "src/invoke/invocation-result.listener.spec.ts",
      "action": "modify",
      "reason": "Tests for crash recovery paths"
    },
    {
      "path": "src/queue/sqlite-job-queue.ts",
      "action": "modify",
      "reason": "Smart stale job detection: check ai-done.json + ai.pid"
    },
    {
      "path": "src/queue/sqlite-job-queue.spec.ts",
      "action": "modify",
      "reason": "Tests for smart stale detection"
    },
    {
      "path": "src/invoke/claude-invocation.service.ts",
      "action": "modify",
      "reason": "Add timeout to waitForResult()"
    },
    {
      "path": "src/invoke/claude-invocation.service.spec.ts",
      "action": "modify",
      "reason": "Timeout expiry test"
    }
  ],
  "steps": [
    {
      "id": "S1",
      "summary": "Add recoverResult() to InvocationResultListener with duplicate-delivery guard",
      "acceptance": [
        "recoverResult() looks up task by jobId, reads current phase/status, and advances pipeline via InternalAdapterService if not already advanced",
        "Duplicate result for already-advanced phase is logged and discarded",
        "Missing task for jobId logs error and returns early",
        "Unit tests cover happy path, duplicate delivery, and no-task-found"
      ],
      "depends_on": []
    },
    {
      "id": "S2",
      "summary": "Smart stale job detection in SqliteJobQueue.onModuleInit()",
      "acceptance": [
        "Jobs with existing ai-done.json are treated as completed results and trigger recovery",
        "Jobs with live ai.pid (process.kill(pid, 0) succeeds) stay in processing status",
        "Jobs with no done file and no live PID are marked failed",
        "Jobs with no taskDir in payload are marked failed",
        "Unit tests cover live PID, done file, truly stale, and no-taskDir scenarios"
      ],
      "depends_on": []
    },
    {
      "id": "S3",
      "summary": "Add configurable timeout to waitForResult()",
      "acceptance": [
        "waitForResult() rejects with TimeoutError after CLAUDE_TIMEOUT_SECS + 100s buffer (~3,700,000 ms)",
        "On timeout, the pending map entry is deleted",
        "Unit test verifies timeout rejection and map cleanup"
      ],
      "depends_on": []
    }
  ],
  "risks": [
    {
      "risk": "Dependency on AW-4 (#440) which hasn't merged yet",
      "mitigation": "Plan assumes AW-4 structures exist; implement after AW-4 merges"
    },
    {
      "risk": "PID recycling false positive on process.kill(pid, 0)",
      "mitigation": "Combine PID check with job age heuristic (lockedAt age > timeout)"
    }
  ],
  "assumptions": [
    "AW-4 (#440) will introduce InvocationResultListener with a pending resolver Map<jobId, resolver> and waitForResult() method",
    "ai-done.json and ai.pid files are written to {taskDir}/meta/ by the AI worker process",
    "InternalAdapterService has a handlePhaseResult() or equivalent method for advancing the pipeline"
  ],
  "open_questions": [
    "Should recoverResult() emit a WAL entry for observability, or is logging sufficient?",
    "Should the PID age heuristic use CLAUDE_TIMEOUT_SECS or a separate config value?"
  ]
}
Plan