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

6.5 KiB

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