---
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`.
