Files
kandev/docs/relation-events.md
T
2026-01-17 19:02:22 +00:00

125 lines
6.5 KiB
Markdown

# Relationship Events LLD
This document defines backend-driven state propagation and how WS notifications mirror those changes to the UI. Clients do **not** emit these events.
## Core Rules
1) **Session state changes drive task state changes**.
2) **Agentctl lifecycle changes drive session state changes**, which then drive task state changes.
3) **Task state changes may move columns**, but only between **In Progress** and **Review** for now.
All state propagation happens **server-side** on internal events. WebSocket events are outbound mirrors for UI synchronization.
---
## 1) Session State -> Task State
**Trigger (backend only)**
- `events.TaskSessionStateChanged` (internal event bus)
**Flow**
1. Resolve `task_id` from `session_id`.
2. Map `TaskSessionState -> TaskState` (table below).
3. If task state changes, call `taskSvc.UpdateTaskState(taskID, newState)`.
4. Emit `events.TaskStateChanged` and WS `task.state_changed`.
5. Emit WS `session.state_changed` for clients.
**Mapping Table**
| TaskSessionState | TaskState | Board Column |
|-------------------|---------------|-----------------------|
| CREATED | SCHEDULING | In Progress (if any) |
| STARTING | SCHEDULING | In Progress (if any) |
| RUNNING | IN_PROGRESS | In Progress |
| WAITING_FOR_INPUT | REVIEW | Review |
| COMPLETED | REVIEW | Review |
| ERROR | ERROR | (no auto-move) |
| CANCELLED | CANCELLED | (no auto-move) |
Notes:
- `WAITING_FOR_INPUT -> REVIEW` and `COMPLETED -> REVIEW` are the default paths. If a board has no Review column, keep task in its current column.
- If the mapped task state equals current task state, do nothing.
---
## 2) Agentctl State -> Session State -> Task State
**Triggers (backend only)**
- `events.AgentctlStarting`
- `events.AgentctlReady`
- `events.AgentctlError`
**Agentctl -> Session Mapping**
| Agentctl Event | Session State |
|--------------------------|---------------|
| AgentctlStarting | STARTING |
| AgentctlReady | RUNNING |
| AgentctlError | FAILED |
**Flow**
1. On Agentctl event, update session state in DB.
2. Emit `events.TaskSessionStateChanged` (which triggers session->task mapping).
3. Emit WS `session.agentctl_*` and `session.state_changed`.
---
## 3) Task State -> Column Move
**Trigger (backend only)**
- `events.TaskStateChanged`
**Policy (for now)**
- Only move tasks **between In Progress and Review**.
- Do not auto-move for other states.
**Flow**
1. When task state changes:
- If new state is `IN_PROGRESS`, move to column mapped to In Progress (if exists).
- If new state is `REVIEW`, move to column mapped to Review (if exists).
2. If column lookup fails or column already matches, do nothing.
3. Emit `events.TaskUpdated` and WS `task.updated` with new `column_id`.
---
## WebSocket Notifications (Outbound Only)
- `session.state_changed`
- `session.agentctl_starting`
- `session.agentctl_ready`
- `session.agentctl_error`
- `task.state_changed`
- `task.updated`
Clients should treat these as **read-only state mirrors**.
---
## E2E Flow (Client-Initiated)
| Edge Trigger (Client) | Backend Action | Internal Event(s) | WS Notification(s) |
|----------------------------------------------|---------------------------------------------------|--------------------------------------|-------------------------------------------------|
| `POST /api/v1/tasks` (start_agent: true) | Create task + create session + start agentctl | task.created, task_session.created | task.created, session.agentctl_starting |
| `POST /api/v1/tasks` (start_agent: false) | Create task + create session (idle) | task.created, task_session.created | task.created |
| `message.add` (session message) | Persist message + prompt agent via ACP | message.added | session.message.added |
| `task.state` (manual update) | Update task state | task.state_changed | task.state_changed, task.updated (column move) |
| `task.session.resume` | Resume session + start agentctl | task_session.state_changed | session.agentctl_starting, session.state_changed |
Notes:
- Column move only occurs for `IN_PROGRESS` / `REVIEW` transitions.
---
## E2E Flow (Agent / agentctl-Initiated)
| Edge Trigger (Agent/agentctl) | Backend Action | Internal Event(s) | WS Notification(s) |
|----------------------------------------------|---------------------------------------------------|--------------------------------------|-------------------------------------------------|
| Agentctl starting | Update session state → STARTING | task_session.state_changed | session.agentctl_starting, session.state_changed |
| Agentctl ready | Update session state → RUNNING | task_session.state_changed | session.agentctl_ready, session.state_changed |
| Agentctl error | Update session state → ERROR | task_session.state_changed | session.agentctl_error, session.state_changed |
| ACP content/progress | Create/append message chunks | message.added / message.updated | session.message.added / session.message.updated |
| ACP input_required | Create message (requests_input=true) | message.added | session.message.added + session.waiting_for_input |
| Git status stream update | Update session git status | git.status.updated | session.git.status |
| Workspace file change stream | Update session file activity | workspace.file.changed | session.workspace.file.changes |
| Shell output stream | Append shell output | shell.output | session.shell.output |