bots-garden/sidekickspublic Fork
⑂ feature/initialize
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/sidekicks.git
git clone ssh://git@rickub.com/bots-garden/sidekicks.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP
architecture.md · 36 lines · 2.8 KBmarkdown
Blame HistoryOpen raw

Architecture — explanation

What is this about?

Before writing the application, the project fixed how its code is divided, what is stored where, and how the Go side informs the interface. This page tells the story; the precise package list, data model, storage keys and event catalogue are in ADR 0003.

Packages and dependencies

The diagram's source is packages.drawio. On 29 October 2026 only the schema package exists; the others are the plan for the next milestones.

Why this split

Three facts drove it:

  • An agent is shared. Alice may be in three channels at once, but she is one process with one model in memory. So one package owns the agent processes (internal/agents) and another owns channels and routing (internal/channels); a channel asks for "a session with Alice", it never starts her.
  • The interface must not know ACP. If the Svelte code handled ACP messages, changing protocol version or agent would ripple into the interface. Everything the interface sees goes through internal/app, as Sidekicks' own calls and events.
  • The configuration can change while agents run. It is loaded, validated and watched in one place (internal/config), which tells the others.

What lives in the YAML and what lives in the database

The YAML file describes what exists: agents, limits, preferences, and channels to create. It is meant to be read and edited by a person. What happens — the channels created from the interface, who joined them, the messages — is stored by the application in Pebble, an embedded key-value store. A channel declared in YAML is created once at startup and then lives in the database like any other, so that editing the YAML never deletes a conversation by accident.

Each configuration file gets its own database: switching from "my work agents" to "my writing agents" never mixes their histories.

Messages are written once, at the end

While an agent answers, the answer lives in memory and the interface receives each fragment. It is written to disk when the turn ends. Writing fifty times a second would wear the disk for nothing; the price is losing an unfinished answer if the application crashes, which is acceptable.

Rejected alternatives

  • Storing conversations in the YAML file: it would turn a file people edit into a database, and conflict with the application writing to it.
  • One package per Wails service with logic inside: it would mix interface concerns with the domain and make it untestable without a window.

How it relates to the rest

The protocol side is explained in Why ACP, the interface side in The interface stack, and the file format is described in the configuration reference.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Architecture — explanation

## What is this about?

Before writing the application, the project fixed how its code is divided, what is stored where, and how the Go side informs the interface. This page tells the story; the precise package list, data model, storage keys and event catalogue are in [ADR 0003](../../adr/0003-architecture.md).

![Packages and dependencies](../../diagrams/packages.png)

The diagram's source is [packages.drawio](../../diagrams/packages.drawio). On 29 October 2026 only the `schema` package exists; the others are the plan for the next milestones.

## Why this split

Three facts drove it:

- **An agent is shared.** Alice may be in three channels at once, but she is one process with one model in memory. So one package owns the agent processes (`internal/agents`) and another owns channels and routing (`internal/channels`); a channel asks for "a session with Alice", it never starts her.
- **The interface must not know ACP.** If the Svelte code handled ACP messages, changing protocol version or agent would ripple into the interface. Everything the interface sees goes through `internal/app`, as Sidekicks' own calls and events.
- **The configuration can change while agents run.** It is loaded, validated and watched in one place (`internal/config`), which tells the others.

## What lives in the YAML and what lives in the database

The YAML file describes **what exists**: agents, limits, preferences, and channels to create. It is meant to be read and edited by a person. What **happens** — the channels created from the interface, who joined them, the messages — is stored by the application in Pebble, an embedded key-value store. A channel declared in YAML is created once at startup and then lives in the database like any other, so that editing the YAML never deletes a conversation by accident.

Each configuration file gets its own database: switching from "my work agents" to "my writing agents" never mixes their histories.

## Messages are written once, at the end

While an agent answers, the answer lives in memory and the interface receives each fragment. It is written to disk when the turn ends. Writing fifty times a second would wear the disk for nothing; the price is losing an unfinished answer if the application crashes, which is acceptable.

## Rejected alternatives

- **Storing conversations in the YAML file**: it would turn a file people edit into a database, and conflict with the application writing to it.
- **One package per Wails service with logic inside**: it would mix interface concerns with the domain and make it untestable without a window.

## How it relates to the rest

The protocol side is explained in [Why ACP](why-acp.md), the interface side in [The interface stack](ui-stack.md), and the file format is described in the [configuration reference](../reference/configuration.md).