Skip to content

Agent task console

Status

Proposed — not started (2026-06-21). A thin, opinionated web UI for the IRC-based agent task system (see irc-ai-agents). It builds on the existing dashboard-backend (already joins #firehose and streams SSE) and the task state machine documented in vibes/irc/agent-controller/docs/task-states.{md,d2}. It complements The Lounge — it is explicitly not a general IRC client and does not replace one.

This plan is the design; nothing here is built yet.

Summary

A purpose-built console that presents the agent task system as tasks, not as raw IRC channels: a status board (pending(draft) → ready → in_progress → for_review → done), a per-task view with the agent transcript + live tool activity, and a small set of controller-mediated actions (create / ready / complete / reopen / archive, edit description).

It is a task dashboard that happens to use IRC as transport. The read model is the #firehose event stream plus channel CHATHISTORY; writes are the task commands the agent-controller already validates. There is no new system of record — ergo remains canonical (irc-ai-agents — a claim since retired by agent-dispatch-unification).

Guiding principle: stay thin by staying read-mostly. The expensive part of any web IRC client is per-user identity (auth → a per-user bouncer session). This console defers that: all writes route through one privileged backend identity, attributing the acting human in-band. Real per-user IRC identity is an explicit, optional later phase — and the reason The Lounge stays around.

Goals

  • One screen that answers “what is the agent system doing right now” far better than scanning channels in The Lounge.
  • Present the lifecycle natively: status columns, draft-prep, the review gate, and capacity at a glance (active vs MAX_ACTIVE_TOTAL, ready-queue depth).
  • Per-task detail: description, live agent transcript (human/agent messages + → / ✓ / ✗ tool calls), a status timeline (from <status>_at METADATA + firehose), and the controller actions as buttons.
  • Reuse what exists: dashboard-backend’s firehose read model + SSE, ergo’s CHATHISTORY/METADATA, the controller as the sole write authority. Minimal new code.
  • Decoupled and swappable: the firehose vocabulary + the controller’s commands are the contract, exactly like every other client.

Non-goals (what keeps it thin)

  • Not a general IRC client. No arbitrary channel join, no DCC, no user/channel modes, no multi-network, no nick management. One network (ergo); channels = tasks; nothing else is shown.
  • Not a replacement for The Lounge. The Lounge stays as the “chat as yourself / free-form” escape hatch so the console never has to grow a full bouncer.
  • No per-user IRC identity in v1 (see “Identity — the cost fork”).
  • No mobile push, message editing/redaction, or offline support in v1.
  • Not a kanban/PM tool. Status columns, not swimlanes-and-sprints.

Identity — the cost fork

The single decision that sets the whole effort:

  • v1 — controller-mediated, one backend identity. The backend connects to ergo as a single nick (e.g. console) and issues task commands / posts clarifications on behalf of the web user, carrying the human’s name in-band (<alice> use the staging branch). The worker already treats any non-self, non-controller PRIVMSG as a human clarification turn, so this “just works”; the attribution lives in the text. Cheap, no bouncer.
  • Phase 4 (optional, ~10× the cost) — real per-user identity. SSO → a per-user ergo SASL session held by the backend (bouncer-lite): true attribution, presence, per-user ACLs. Only build this if “humans converse as themselves from the web UI” becomes a hard requirement. It is the prerequisite for ever enabling require-sasl on ergo (ties into kanidm).

Requester attribution is the one wrinkle v1 must answer: !new records the sender as requester, so console-created tasks would all read requester = console. Options (decide in Phase 2): (a) accept console and show the real user only in the console’s own audit; (b) a small controller extension — a trusted console nick may prefix !new/actions with [as <nick>] and the controller uses that as the requester. Lean (b)-lite for honest attribution.

Architecture

┌────────────────────────┐ ┌──────────────────────────────┐
│ Browser SPA │ │ The Lounge (unchanged) │
│ • status board │ │ free-form chat / "as you" │
│ • task detail + xcript │ │ escape hatch │
│ • action buttons │ └───────────────┬──────────────┘
└──────────┬─────────────┘ │ IRC
│ HTTP + SSE (or WS) ▼
▼ ┌─────────────┐
┌────────────────────────────┐ IRC (read │ ergo │
│ console-backend │ + write) │ channels, │
│ (extends dashboard-backend)│◀────────────▶│ CHATHISTORY│
│ • joins #firehose (read) │ │ METADATA │
│ • CHATHISTORY proxy │ └──────┬──────┘
│ • command/message proxy │ │ IRC
│ as `console` identity │ ▼
│ • in-memory task state │ ┌──────────────────┐
└────────────────────────────┘ │ agent-controller │ (sole
│ write authority │ state owner)
└──────────────────┘

The console-backend is a read-model + command-proxy, the shape dashboard-backend already is — not a bouncer. The only new privilege is that it can write (post task commands), which changes its trust posture (see Security).

Backend (extend dashboard-backendconsole-backend)

The read half exists. Add:

  • GET /api/tasks — snapshot of current task state (from the firehose in-memory view + METADATA). Already largely present.
  • GET /api/events — SSE live stream (already present; the board subscribes).
  • GET /api/tasks/:id/historyCHATHISTORY proxy for #task-<id>: returns the conversation (human/agent messages, →/✓/✗ tool lines, [PERM]), paginated. New.
  • POST /api/tasks {desc, as}!new in #tasks (creates a draft). New.
  • POST /api/tasks/:id/actions {action: ready|complete|reopen|archive|kill, as} → the matching !-command in #task-<id>. New.
  • PUT /api/tasks/:id/description {text, as}!desc <text> (while draft).
  • POST /api/tasks/:id/messages {text, as} → PRIVMSG #task-<id> as <as> text (clarification). New.

Writes require a registered ergo identity for the backend (unlike the anonymous read-only dashboard) and auth in front (see Security). Keep it small and in Go to match agent-controller/dashboard-backend.

Data model / state

Entirely derived — no store of its own:

  • Board state ← the firehose in-memory view (tasks keyed by id, each with status, title, requester, assignee, timestamps). Live via status / task_* events.
  • Per-task transcriptCHATHISTORY of #task-<id> on open, then live PRIVMSGs + firehose tool_call_* / status merged into one timeline.
  • Timeline / cycle time ← the <status>_at METADATA markers + firehose status events.
  • The mapping is exactly docs/task-states.md — that file is the contract; this console renders it.

Frontend

Opinionated, small. Suggested stack: Svelte or SolidJS (tiny, fast, no heavy runtime); plain CSS with the palette already drafted for the IRC theme. Component tree:

  • Board — columns by status (ready, in_progress, for_review, blocked; pending drafts and recent done/archived collapsible). A capacity header: N/MAX_ACTIVE_TOTAL active · M ready — the one number no current view shows.
  • Task card — title, status pill (colors from the state spec), assignee (friendly worker nick), age, a for_review badge that begs a review.
  • Task detail (drawer/route) — description (with !desc edit while draft), transcript with tool-call rows rendered distinctly, status timeline, and the action buttons gated by status (!ready on a draft, !complete/!reopen on for_review, !kill on in_progress, !archive).
  • Draft-prep flow!new opens a draft card with the readiness checklist inline; !ready is the prominent CTA once a description exists.
  • Composer — posts clarifications to the task channel (v1: as the console identity, prefixed with the web user).

Optimistic UI optional: actions can render immediately and reconcile when the authoritative firehose status event arrives (the controller remains the truth).

Security / trust

  • The backend gains write power (can create tasks, drive lifecycle, post as humans), so it must sit behind auth — the platform internal Gateway (Envoy) with forward-auth/OIDC, or app-level auth. (The Lounge is already behind that Gateway at irc.rrchnm.internal; the console gets its own HTTPRoute, e.g. tasks.rrchnm.internal — the public route already noted as a follow-up in plan-irc-ai-agents.)
  • The backend needs a registered ergo account (console) materialized via ESO, like the controller’s. It is not anonymous (the read-only dashboard is).
  • The #firehose +m / sole-publisher ACL hardening (still open in plan-irc-ai-agents) matters more here: the console trusts firehose integrity.
  • Decide whether read and write share one service (simpler, but couples the read-only dashboard’s trust with write power) or split into a read-only dashboard-backend + a separate privileged console-backend. Lean: one service, auth-gated writes, read endpoints unauthenticated within the cluster.

Deployment

  • Extend vibes/irc/dashboard-backend/ (or fork to console-backend); Go image to ghcr.io/chnm/systems/vibes/irc/<name>, :main, mirrored to Zot.
  • k8s Deployment in vibes; HTTPRoute on the internal Gateway; SAN + hostname wiring like The Lounge (routes.yaml, internal TLS cert, k0s-gateways.yaml). netpol: vibes-isolation already allows same-namespace → ergo.
  • Frontend can ship as static assets served by the same Go binary.

Open questions

  • Requester attribution for console-created tasks (see Identity) — accept console, or add the trusted [as <nick>] controller extension.
  • Auth mechanism — Gateway forward-auth/OIDC vs. app-level; ties to kanidm and is the on-ramp to Phase 4 per-user identity.
  • One service or two (read-only dashboard vs. privileged console-backend).
  • Optimistic vs. server-confirmed UI for actions.
  • CHATHISTORY limits/pagination for long task transcripts; lazy-load older.
  • Notifications — in-app only in v1; push (for_review ready to review, blocked) reuses the deferred push-bridge from plan-irc-ai-agents.

Implementation order

  1. Read-only board. Modern SPA on the existing snapshot + SSE: status columns + capacity header. No writes. Biggest UX win per unit effort; validates the firehose-as-UI-state model. (~days)
  2. Task detail + transcript. CHATHISTORY proxy endpoint; per-task view merging history + live events; status timeline. (~days)
  3. Controller-mediated actions. Auth in front; backend gains a console identity; buttons for !new/!desc/!ready/!complete/!reopen/ !archive/!kill; settle requester attribution. (~1 week)
  4. In-app clarifications / chat. Composer posts to the task channel as the console identity (prefixed with the web user); optimistic send. For this to be real “chat about the work” and not just answering [[BLOCKED]] prompts, the agent must stay reachable / remember — see the “Agent conversation” section of plan-irc-ai-agents. (~days)
  5. (Optional, expensive) Real per-user identity. SSO → per-user ergo SASL sessions; true attribution + presence. Only if needed. (~weeks)
  6. (Optional) Polish. Push notifications, search, mobile/PWA, the “animated workspace” flourish.

A read-only board + task detail (steps 0–1) is a compelling MVP on its own and is mostly frontend on top of code that already exists.

Relationship to irc-ai-agents

  • Subsumes that plan’s dashboard frontend evolution (its step 13, “timeline → animated/Godot”) with something more useful and task-native.
  • Reuses its already-built read backend (steps 9–10) and the frozen firehose vocabulary + METADATA — no controller or event-contract changes for steps 0–1.
  • Inherits its open items that this console leans on: #firehose +m hardening (integrity), the public HTTPRoute, and per-user identity (its “ephemeral identities can’t be IaC’d” wrinkle is exactly Phase 4 here).