--- status: accepted date: 2026-05-03 decision-makers: [janpetervisser] --- # ADR-0007: Agent ↔ user question channel via persistent table + LISTEN/NOTIFY ## Context and Problem Statement When Claude Code is executing a task and needs human input, it must be able to pause, post a question, and receive an answer — potentially across separate sessions. The app must notify an active user that a question is waiting. How should this async communication channel be designed? ## Decision Drivers - Questions must survive agent session restarts (persistent, not in-memory). - The app user needs a real-time notification without polling from the client. - The infrastructure already includes PostgreSQL with LISTEN/NOTIFY (used for M8 realtime updates). - Answers must be readable by the agent in a future session without the original connection. ## Considered Options - **Synchronous polling only** — agent polls an endpoint every N seconds. - **Push via SSE without persistence** — agent opens SSE connection, user pushes answer over it. - **Persistent `claude_questions` table + PostgreSQL LISTEN/NOTIFY** ## Decision Outcome Chosen option: **Persistent table + LISTEN/NOTIFY**, because it is the only option that survives session restarts on both ends and reuses existing infrastructure. ### Consequences - Good, because questions survive agent and user session restarts. - Good, because reuses the `scrum4me_changes` LISTEN/NOTIFY channel already in place. - Good, because any product member with access can answer, not just the original session. - Bad, because adds a `claude_questions` table and trigger to the schema. - Bad, because LISTEN/NOTIFY requires a persistent DB connection (`DIRECT_URL` env var). ### Confirmation `docs/patterns/claude-question-channel.md` documents the full implementation. MCP tools: `ask_user_question`, `get_question_answer`, `list_open_questions`, `cancel_question`. ## Pros and Cons of the Options ### Synchronous polling only - Good, because simple — no extra infrastructure. - Bad, because agent blocks a CPU slot while polling; question lost if agent restarts. - Bad, because no real-time notification to the user. ### Push via SSE without persistence - Good, because low latency. - Bad, because agent-side SSE connection is fragile across restarts. - Bad, because no persistence — if the user isn't connected at the moment the question is posted, it is lost. ### Persistent table + LISTEN/NOTIFY - Good, because fully durable. - Good, because real-time notification to the user reuses existing M8 infrastructure. - Neutral, because requires `DIRECT_URL` for a persistent PostgreSQL connection (already required for M8). ## More Information See `docs/plans/M11-claude-questions.md` and `docs/patterns/claude-question-channel.md`.