docs(adr): add 0007-claude-question-channel-design

This commit is contained in:
Janpeter Visser 2026-05-03 01:11:12 +02:00
parent b77d84db5a
commit eb9c843b6e

View file

@ -0,0 +1,64 @@
---
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`.