# duckbugio/flock architecture

> Architecture diagram of the GitHub repository duckbugio/flock, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-18). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/duckbugio/flock
- Repository: https://github.com/duckbugio/flock

## Overview

1. **Monorepo split into platform-agnostic core and channel adapters.**  
   The central architecture is in `core/`, which contains the Claude-driven agent orchestration, workspace management, polling, git/PR integration, voice handling, and shared chat logic. `adapters/telegram/` and `adapters/vk/` are thin transport-specific front ends that translate a chat platform’s events into the core’s internal chat/service model. The repo is set up so new transports can be added under `adapters/<name>/` without changing the core.

2. **Two runnable bot entrypoints.**  
   `cmd/flock-telegram` and `cmd/duck-vk` are the deployable binaries. Each wires configuration, the adapter, and the shared core together. The Telegram path is the primary product and is packaged with Docker, Compose, and Ansible deployment assets under `adapters/telegram/deploy/`.

3. **Core control plane: chat service plus orchestration pipeline.**  
   `core/chat/` is the main request lifecycle: it accepts incoming chat events, applies guardrails, manages the outbox/progress/nudge behavior, chunks and routes messages, and coordinates agent work. `core/dispatch/` and `core/poller/` drive event routing and background polling loops, while `core/session/` keeps per-chat state. This is the boundary where a human message becomes either a normal answer or a multi-step development run.

4. **Claude Code execution is isolated behind a dedicated runner layer.**  
   `core/claude/` wraps the Claude Code CLI, including streaming/tailing, JSONL decoding, MCP support, and tests with fake transcripts. This layer is the bridge from orchestration into actual model execution. The README’s “Claude dev team” is implemented as subagents in `core/agents/`, with the pipeline roles planner → coder → tester → reviewer → arbiter.

5. **Per-chat workspace isolation is a first-class subsystem.**  
   `core/workspace/` creates and manages isolated directories per chat, matching the repo’s model of `/workspace/chat_<id>`. The bot uses these workspaces to ensure that different chats cannot see each other’s files and can run in parallel up to a concurrency limit. `core/gitsetup/` complements this by preparing repos/branches for agent work and PR creation.

6. **Transport adapters focus on message shape, rich content, and platform-specific constraints.**  
   `adapters/telegram/` contains Telegram-specific command handling, message formatting, rich text transport, upload/voice helpers, and the bot bootstrap. `adapters/vk/` contains the equivalent VK logic: long polling, API calls, mention detection, payload handling, and media upload. The core chat layer stays transport-neutral; each adapter maps platform events into the core and maps responses back into the platform’s formatting rules.

7. **Media and voice are separate processing paths, not just text extensions.**  
   `core/voice/` handles transcription and voice-provider abstraction, with `adapters/telegram/voice.go` and `adapters/vk/voice.go` integrating platform uploads/attachments. This means voice messages enter the system through a dedicated decode/transcribe path before they become normal chat commands or prompts. The README notes multiple providers, including remote APIs and local mode.

8. **Git host integration is asynchronous and poll-based.**  
   The system supports Gitea/GitHub/GitLab-style flows through `core/ghstar/`, `core/poller/`, and likely the git-related utilities around the bot’s run lifecycle. A key design choice is that PR review comments are handled by outbound polling instead of inbound webhooks, so the bot can operate even when the git host cannot reach it. This matches the architecture’s “chat triggers work, poller closes the loop” model.

9. **Guardrails and cost/rate controls sit inside the core, not the adapter.**  
   `core/ratelimit/`, `core/cost/`, `core/pending/`, `core/nudge/`, and `core/chat/guards.go` enforce safety, throttling, and run bookkeeping. These are the boundaries that keep the agent from looping forever, spamming replies, or running uncontrolled. The arbiter role in the team is the semantic loop-breaker; these packages are the operational guardrails.

10. **Shared internal utilities support safe file and config handling.**  
    `internal/atomicfile/` provides atomic writes, `internal/fsutil/` handles upload/file utilities, and `internal/config/` centralizes environment/config parsing. These are low-level support packages used across the bot and deployment paths to keep state changes safe and consistent.

11. **Deployment is image-first, with optional orchestration sidecars.**  
    The Telegram adapter ships with its own `Dockerfile`, `docker-compose.yml`, and Ansible role/playbook templates. The default deployment runs as a prebuilt container, with optional sidecars for Docker-in-Docker CI tooling and Caddy for webhook/TLS ingress. This makes runtime packaging part of the adapter boundary rather than the core application logic.

12. **Testing is broad and component-level.**  
    Most subsystems have focused unit tests beside the code, and `core/claude/` includes integration-style fixtures for CLI transcript decoding. That suggests the architecture is built around deterministic orchestration and adapter translation, with tests validating message routing, workspace behavior, and external integration wrappers rather than only end-to-end bot runs.

13. **Conceptual data flow for diagramming.**  
    A user message enters a transport adapter, is normalized into a core chat event, passes through session/guard/rate-limit logic, and is either answered directly or routed into the Claude team pipeline. If it becomes a development task, the core creates/uses an isolated workspace, runs Claude Code subagents, writes files, tests, and git operations, then posts progress back through the adapter. Background pollers later bring PR review comments back into the originating chat.

14. **Primary architectural boundaries to draw.**  
    The most important boundaries are: transport vs. core, chat orchestration vs. Claude execution, per-chat workspace isolation vs. shared deployment secrets, and synchronous chat handling vs. asynchronous polling/background reconciliation. These are the lines that define how the system stays multi-tenant, multi-transport, and safe to run on one server.

Main entry points: [`core/chat/service.go`](https://github.com/duckbugio/flock/blob/main/core/chat/service.go) (Chat service), [`adapters/telegram/bot.go`](https://github.com/duckbugio/flock/blob/main/adapters/telegram/bot.go) (Telegram bot), [`adapters/vk/bot.go`](https://github.com/duckbugio/flock/blob/main/adapters/vk/bot.go) (VK bot), [`core/voice/voice.go`](https://github.com/duckbugio/flock/blob/main/core/voice/voice.go) (Voice core)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_g_cmd["Binaries"]
  node_n_cmd_telegram["flock-telegram<br/>binary entrypoint<br/>[main.go]"]
  node_n_cmd_vk["duck-vk<br/>binary entrypoint<br/>[main.go]"]
end

subgraph group_g_adapters["Adapters"]
  node_n_tg_bot["Telegram bot<br/>adapter bootstrap<br/>[bot.go]"]
  node_n_tg_rich["Rich transport<br/>telegram formatting<br/>[richtransport.go]"]
  node_n_tg_voice["Telegram voice<br/>media bridge<br/>[voice.go]"]
  node_n_tg_deploy["Telegram deploy<br/>deployment assets<br/>[playbook.yml]"]
  node_n_vk_bot["VK bot<br/>adapter bootstrap<br/>[bot.go]"]
  node_n_vk_api["VK API<br/>transport api<br/>[api.go]"]
  node_n_vk_voice["VK voice<br/>media bridge<br/>[voice.go]"]
end

subgraph group_g_core["Core"]
  node_n_chat["Chat service<br/>orchestration pipeline<br/>[service.go]"]
  node_n_dispatch["Dispatch<br/>event router<br/>[dispatch.go]"]
  node_n_session[("Session state<br/>per-chat state<br/>[session.go]")]
  node_n_claude["Claude runner<br/>cli runner<br/>[runner.go]"]
  node_n_agents["Agent team<br/>subagents<br/>[planner.md]"]
  node_n_workspace[("Workspace<br/>isolation layer<br/>[workspace.go]")]
  node_n_gitsetup["Git setup<br/>repo prep<br/>[gitsetup.go]"]
  node_n_poller(("Poller<br/>background loop<br/>[poller.go]"))
  node_n_ghstar["Git host<br/>git integration<br/>[ghstar.go]"]
  node_n_voice["Voice core<br/>transcription layer<br/>[voice.go]"]
end

subgraph group_g_support["Support"]
  node_n_internal_config["Config<br/>[config.go]"]
  node_n_internal_fsutil["FS utils<br/>file utilities<br/>[upload.go]"]
  node_n_internal_atomicfile["Atomic file<br/>safe writes<br/>[atomicfile.go]"]
end

node_n_cmd_telegram -->|"boots"| node_n_tg_bot
node_n_cmd_telegram -->|"wires"| node_n_chat
node_n_cmd_telegram -->|"loads"| node_n_internal_config
node_n_cmd_vk -->|"boots"| node_n_vk_bot
node_n_cmd_vk -->|"wires"| node_n_chat
node_n_cmd_vk -->|"loads"| node_n_internal_config
node_n_tg_bot -->|"formats"| node_n_tg_rich
node_n_tg_bot -->|"handles media"| node_n_tg_voice
node_n_tg_bot -->|"sends events"| node_n_chat
node_n_tg_bot -->|"uploads"| node_n_internal_fsutil
node_n_tg_voice -->|"transcribes"| node_n_voice
node_n_vk_bot -->|"calls"| node_n_vk_api
node_n_vk_bot -->|"handles media"| node_n_vk_voice
node_n_vk_bot -->|"sends events"| node_n_chat
node_n_vk_voice -->|"transcribes"| node_n_voice
node_n_chat -->|"tracks"| node_n_session
node_n_chat -->|"routes"| node_n_dispatch
node_n_chat -->|"isolates"| node_n_workspace
node_n_chat -->|"executes"| node_n_claude
node_n_chat -->|"coordinates"| node_n_agents
node_n_chat -->|"prepares repos"| node_n_gitsetup
node_n_chat -->|"decodes voice"| node_n_voice
node_n_chat -->|"writes safely"| node_n_internal_atomicfile
node_n_poller -->|"reconciles"| node_n_ghstar
node_n_poller -->|"feeds updates"| node_n_chat
node_n_gitsetup -->|"uses"| node_n_workspace
node_n_gitsetup -->|"syncs"| node_n_ghstar
node_n_claude -->|"runs roles"| node_n_agents
node_n_claude -->|"reads/writes"| node_n_workspace
node_n_voice -->|"uses"| node_n_internal_fsutil
node_n_tg_deploy -->|"packages"| node_n_tg_bot
node_n_internal_config -->|"configures"| node_n_chat

click node_n_cmd_telegram "https://github.com/duckbugio/flock/blob/main/cmd/flock-telegram/main.go"
click node_n_cmd_vk "https://github.com/duckbugio/flock/blob/main/cmd/duck-vk/main.go"
click node_n_tg_bot "https://github.com/duckbugio/flock/blob/main/adapters/telegram/bot.go"
click node_n_tg_rich "https://github.com/duckbugio/flock/blob/main/adapters/telegram/richtransport.go"
click node_n_tg_voice "https://github.com/duckbugio/flock/blob/main/adapters/telegram/voice.go"
click node_n_tg_deploy "https://github.com/duckbugio/flock/blob/main/adapters/telegram/deploy/playbook.yml"
click node_n_vk_bot "https://github.com/duckbugio/flock/blob/main/adapters/vk/bot.go"
click node_n_vk_api "https://github.com/duckbugio/flock/blob/main/adapters/vk/api.go"
click node_n_vk_voice "https://github.com/duckbugio/flock/blob/main/adapters/vk/voice.go"
click node_n_chat "https://github.com/duckbugio/flock/blob/main/core/chat/service.go"
click node_n_dispatch "https://github.com/duckbugio/flock/blob/main/core/dispatch/dispatch.go"
click node_n_session "https://github.com/duckbugio/flock/blob/main/core/session/session.go"
click node_n_claude "https://github.com/duckbugio/flock/blob/main/core/claude/runner.go"
click node_n_agents "https://github.com/duckbugio/flock/blob/main/core/agents/planner.md"
click node_n_workspace "https://github.com/duckbugio/flock/blob/main/core/workspace/workspace.go"
click node_n_gitsetup "https://github.com/duckbugio/flock/blob/main/core/gitsetup/gitsetup.go"
click node_n_poller "https://github.com/duckbugio/flock/blob/main/core/poller/poller.go"
click node_n_ghstar "https://github.com/duckbugio/flock/blob/main/core/ghstar/ghstar.go"
click node_n_voice "https://github.com/duckbugio/flock/blob/main/core/voice/voice.go"
click node_n_internal_config "https://github.com/duckbugio/flock/blob/main/internal/config/config.go"
click node_n_internal_fsutil "https://github.com/duckbugio/flock/blob/main/internal/fsutil/upload.go"
click node_n_internal_atomicfile "https://github.com/duckbugio/flock/blob/main/internal/atomicfile/atomicfile.go"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_n_cmd_telegram,node_n_cmd_vk toneBlue
class node_n_tg_bot,node_n_tg_rich,node_n_tg_voice,node_n_tg_deploy,node_n_vk_bot,node_n_vk_api,node_n_vk_voice toneAmber
class node_n_chat,node_n_dispatch,node_n_session,node_n_claude,node_n_agents,node_n_workspace,node_n_gitsetup,node_n_poller,node_n_ghstar,node_n_voice toneMint
class node_n_internal_config,node_n_internal_fsutil,node_n_internal_atomicfile toneRose
```

## Components

### Binaries

Deployable entrypoints for the two bot runtimes.

- [flock-telegram](https://github.com/duckbugio/flock/blob/main/cmd/flock-telegram/main.go): binary entrypoint · Primary Telegram bot executable wiring config, adapter, and core. · `cmd/flock-telegram/main.go`
- [duck-vk](https://github.com/duckbugio/flock/blob/main/cmd/duck-vk/main.go): binary entrypoint · VK bot executable wiring config, adapter, and core. · `cmd/duck-vk/main.go`

### Adapters

Transport-specific front ends for chat platforms and deployment packaging.

- [Telegram bot](https://github.com/duckbugio/flock/blob/main/adapters/telegram/bot.go): adapter bootstrap · Telegram command handling, message routing, and bot lifecycle. · `adapters/telegram/bot.go`
- [Rich transport](https://github.com/duckbugio/flock/blob/main/adapters/telegram/richtransport.go): telegram formatting · Telegram-specific rich text, wire format, and content mapping. · `adapters/telegram/richtransport.go`
- [Telegram voice](https://github.com/duckbugio/flock/blob/main/adapters/telegram/voice.go): media bridge · Telegram upload and voice handling before handing off to core voice logic. · `adapters/telegram/voice.go`
- [Telegram deploy](https://github.com/duckbugio/flock/blob/main/adapters/telegram/deploy/playbook.yml): deployment assets · Docker, Compose, Ansible, and ingress templates for Telegram deployment. · `adapters/telegram/deploy/playbook.yml`
- [VK bot](https://github.com/duckbugio/flock/blob/main/adapters/vk/bot.go): adapter bootstrap · VK long-poll bot lifecycle and event handling. · `adapters/vk/bot.go`
- [VK API](https://github.com/duckbugio/flock/blob/main/adapters/vk/api.go): transport api · VK API integration for messaging, uploads, and polling. · `adapters/vk/api.go`
- [VK voice](https://github.com/duckbugio/flock/blob/main/adapters/vk/voice.go): media bridge · VK upload and voice handling before handoff into core voice logic. · `adapters/vk/voice.go`

### Core

Platform-neutral orchestration, agent execution, and shared bot logic.

- [Chat service](https://github.com/duckbugio/flock/blob/main/core/chat/service.go): orchestration pipeline · Main request lifecycle: guardrails, outbox, progress, chunking, and routing. · `core/chat/service.go`
- [Dispatch](https://github.com/duckbugio/flock/blob/main/core/dispatch/dispatch.go): event router · Routes incoming chat events into the core service pipeline. · `core/dispatch/dispatch.go`
- [Session state](https://github.com/duckbugio/flock/blob/main/core/session/session.go): per-chat state · Tracks conversation state and chat-local context. · `core/session/session.go`
- [Claude runner](https://github.com/duckbugio/flock/blob/main/core/claude/runner.go): cli runner · Wraps Claude Code CLI execution, streaming, decoding, and MCP support. · `core/claude/runner.go`
- [Agent team](https://github.com/duckbugio/flock/blob/main/core/agents/planner.md): subagents · Planner, coder, tester, reviewer, and arbiter roles for development runs. · `core/agents/planner.md`
- [Workspace](https://github.com/duckbugio/flock/blob/main/core/workspace/workspace.go): isolation layer · Creates isolated per-chat workspaces for parallel agent runs. · `core/workspace/workspace.go`
- [Git setup](https://github.com/duckbugio/flock/blob/main/core/gitsetup/gitsetup.go): repo prep · Prepares repositories, branches, and PR-friendly worktrees for agent work. · `core/gitsetup/gitsetup.go`
- [Poller](https://github.com/duckbugio/flock/blob/main/core/poller/poller.go): background loop · Asynchronous reconciliation loop for git-host events and review comments. · `core/poller/poller.go`
- [Git host](https://github.com/duckbugio/flock/blob/main/core/ghstar/ghstar.go): git integration · GitHub/Gitea/GitLab-style integration for PR and review workflows. · `core/ghstar/ghstar.go`
- [Voice core](https://github.com/duckbugio/flock/blob/main/core/voice/voice.go): transcription layer · Platform-neutral voice abstraction and transcription providers. · `core/voice/voice.go`

### Support

Low-level shared utilities used across the repo.

- [Config](https://github.com/duckbugio/flock/blob/main/internal/config/config.go): env config · Shared configuration and environment parsing. · `internal/config/config.go`
- [FS utils](https://github.com/duckbugio/flock/blob/main/internal/fsutil/upload.go): file utilities · Shared upload and file helpers. · `internal/fsutil/upload.go`
- [Atomic file](https://github.com/duckbugio/flock/blob/main/internal/atomicfile/atomicfile.go): safe writes · Atomic write helper for durable state changes. · `internal/atomicfile/atomicfile.go`

## Connections

- flock-telegram → Telegram bot: boots · The Telegram binary initializes the Telegram adapter.
- flock-telegram → Chat service: wires · Connects Telegram events into the shared chat service.
- flock-telegram → Config: loads · Reads environment and runtime configuration.
- duck-vk → VK bot: boots · The VK binary initializes the VK adapter.
- duck-vk → Chat service: wires · Connects VK events into the shared chat service.
- duck-vk → Config: loads · Reads environment and runtime configuration.
- Telegram bot → Rich transport: formats · Maps core responses to Telegram rich content.
- Telegram bot → Telegram voice: handles media · Routes Telegram voice and uploads into core voice handling.
- Telegram bot → Chat service: sends events · Normalizes Telegram messages into core chat events.
- Telegram bot → FS utils: uploads · Uses shared file helpers for platform attachments.
- Telegram voice → Voice core: transcribes · Bridges Telegram voice payloads into core voice processing.
- VK bot → VK API: calls · Uses VK API and long-poll plumbing.
- VK bot → VK voice: handles media · Routes VK voice and uploads into core voice handling.
- VK bot → Chat service: sends events · Normalizes VK messages into core chat events.
- VK voice → Voice core: transcribes · Bridges VK voice payloads into core voice processing.
- Chat service → Session state: tracks · Maintains per-chat state across turns.
- Chat service → Dispatch: routes · Dispatches chat events through the orchestration pipeline.
- Chat service → Workspace: isolates · Creates or reuses per-chat workspaces for agent runs.
- Chat service → Claude runner: executes · Invokes Claude Code for model-driven work.
- Chat service → Agent team: coordinates · Selects and sequences planner/coder/tester/reviewer/arbiter roles.
- Chat service → Git setup: prepares repos · Sets up git state for development tasks and PRs.
- Chat service → Voice core: decodes voice · Converts voice input into chat prompts.
- Chat service → Atomic file: writes safely · Persists state and artifacts with atomic updates.
- Poller → Git host: reconciles · Polls git-host state and feeds updates back into the bot.
- Poller → Chat service: feeds updates · Reinjects asynchronous git-host events into chat flows.
- Git setup → Workspace: uses · Prepares repo work inside isolated workspaces.
- Git setup → Git host: syncs · Coordinates git-host metadata for PR workflows.
- Claude runner → Agent team: runs roles · Executes the subagent workflow through Claude Code.
- Claude runner → Workspace: reads/writes · Operates against the per-chat workspace filesystem.
- Voice core → FS utils: uses · Shares file and upload utilities for voice handling.
- Telegram deploy → Telegram bot: packages · Deployment assets ship the Telegram adapter and binary.
- Config → Chat service: configures · Supplies core runtime settings.
