# Paseka — agent configuration corpus > Single-fetch Markdown for configuring bees (YAML, prompts, event emit) without reading the Go codebase. > Index: https://russ-p.github.io/paseka/llms.txt Colony paths: `.paseka/bees/`, `.paseka/prompts/`, `.paseka/runs/`, `.paseka/worktrees/`. Machine-local: `~/.config/paseka//` (secrets only — no prompts). Bus contracts: SIGNAL, INSIGHT, MUTATION, VERIFICATION. --- # Source: guide/bee-config.md --- # Bee role config (`.paseka/bees/.yaml`) A **bee** is a named role bound to an adapter, prompt template, and optional routing / completion rules. Each file under `.paseka/bees/` defines one role. Implementation: [`internal/colony/bee.go`](../../internal/colony/bee.go) (`Bee` struct, `LoadBee`), plus [`command.go`](../../internal/colony/command.go), [`params.go`](../../internal/colony/params.go), [`routing.go`](../../internal/colony/routing.go), [`run_summary.go`](../../internal/colony/run_summary.go), [`completion.go`](../../internal/colony/completion.go), [`bee_validate.go`](../../internal/colony/bee_validate.go). Related: [bee routing](../reference/bee-routing.md) (`subscribes` / `publishes`), [prompt templates](prompt-templates.md), [architecture overview](../architecture/overview.md) (adapters, colony layout). --- ## 1. Files and loading ``` .paseka/bees/ ├── scout.yaml ├── builder.yaml ├── guard.yaml └── builder.local.yaml # optional, gitignored overlay ``` | Path | Purpose | | ---- | ------- | | `.paseka/bees/.yaml` | Canonical role definition (committed) | | `.paseka/bees/.local.yaml` | Machine-local overlay; `prompt_template` and `system_template` applied at resolve time | `paseka` loads bees via `colony.LoadBee(colonyRoot, role)` / `LoadAllBees`: 1. Role must be non-empty and must not contain `/` or `..`. 2. Base file is `.paseka/bees/.yaml` (filename stem = role when `role:` is omitted). 3. Event rules, `run_summary`, and adapter requirements are validated at load time. 4. If `.local.yaml` exists, its `prompt_template` and `system_template` override the base at resolve time (see [prompt templates](prompt-templates.md)). `*.local.yaml` files are listed in `.paseka/.gitignore` and are skipped by `LoadAllBees`. --- ## 2. Schema Go type (`internal/colony/bee.go`): ```go type Bee struct { Role string Adapter string PromptTemplate string SystemTemplate string Sector string Worktree bool Intents []string DefaultIntent string Command Command PostExec Command Params map[string]any Subscribes []SubscriptionRule Publishes []PublicationRule CompletionContract CompletionContract RunSummary RunSummaryPolicy } ``` ### Field reference | YAML field | Required | Meaning | | ---------- | -------- | ------- | | `role` | recommended | Role name. If empty, defaults to the filename stem (`builder.yaml` → `builder`). | | `adapter` | no | `cursor` (default), `pi`, `claude`, or `script`. Unknown names fail load. | | `prompt_template` | usually | Path relative to `.paseka/prompts/`. User/task turn. Optional for `adapter: script` (no colony default applied when omitted). | | `system_template` | no | Path relative to `.paseka/prompts/`. Role / standing instructions injected by the adapter (see [prompt templates](prompt-templates.md)). | | `sector` | no | Default sector name from `colony.yaml` `sectors`. Task `sector` wins when set. | | `worktree` | no | When `true`, adapter cwd is under `.paseka/worktrees//` (plus sector path if any). | | `intents` | no | Explicit intent vocabulary for this bee. When omitted, runtime discovers intents from `_partials/-intent-*.md` prompt partials. | | `default_intent` | no | Default intent when the caller omits `--intent` or passes an unknown value. When omitted, `general` is used if present in the vocabulary; otherwise the first discovered intent. | | `params` | no | Adapter flag map (`model`, `trust`, …). Ignored when `command` is set (runtime warns if both are present). | | `command` | script: **yes** | Full agent argv (string or YAML list). Replaces `params`-based flag mapping. | | `post_exec` | no | Hook after AFK `bee run` and interactive `bee chat`. Failures are logged; they do not fail the bee run. | | `subscribes` | no | Event → dispatch rules. Empty = backward-compatible allow any `task.ready`. See [bee routing](../reference/bee-routing.md). | | `publishes` | no | Advisory expected outputs; undeclared domain publishes warn only (MVP). | | `completion_contract` | no | Hard post-run event requirements; violation fails the run. | | `run_summary` | no | `auto` (default) \| `required` \| `disabled` — controls `INSIGHT/run.summary` synthesis/enforcement. | --- ## 3. Example ```yaml # .paseka/bees/builder.yaml role: builder adapter: cursor sector: frontend params: model: composer-2.5 output_format: stream-json trust: true force: true # Optional: override adapter flag mapping (docker-compose style). # command: agent -p --yolo --workspace $WORKSPACE $PROMPT prompt_template: builder.md # relative to .paseka/prompts/ worktree: true # run inside .paseka/worktrees// subscribes: - type: SIGNAL kind: task.ready dispatch: task publishes: - type: MUTATION kind: code.proposal.isolated ``` Guard with a completion contract: ```yaml # .paseka/bees/guard.yaml role: guard adapter: cursor prompt_template: guard.md params: model: composer-2.5 output_format: stream-json trust: true force: true worktree: true subscribes: - type: MUTATION kind: code.proposal.isolated dispatch: direct publishes: - type: VERIFICATION kind: verification.success - type: VERIFICATION kind: verification.failed completion_contract: required: - type: VERIFICATION kind_one_of: - verification.success - verification.failed count: 1 ``` Hivewright and main-guard (root proposal path): ```yaml # .paseka/bees/hivewright.yaml role: hivewright adapter: cursor worktree: false publishes: - type: MUTATION kind: code.proposal.root # .paseka/bees/main-guard.yaml role: main-guard adapter: cursor worktree: false subscribes: - type: MUTATION kind: code.proposal.root dispatch: direct publishes: - type: VERIFICATION kind: verification.success - type: VERIFICATION kind: verification.failed ``` --- ## 4. Adapters `ResolveAdapter()` defaults empty `adapter` to `cursor`. Allowed values: `cursor`, `pi`, `claude`, `script`. | Adapter | Notes | | ------- | ----- | | `cursor` | Cursor Agent CLI (`agent`). Params map to CLI flags unless `command` is set. With `system_template`, runtime merges system + task into the positional prompt (`$PROMPT`); Pi/Claude use separate append-system flags instead. | | `pi` | Pi CLI (`pi`). Params: `model`, `provider`, `thinking`, `output_format`, `plan`, `binary`. | | `claude` | Claude Code CLI; same params plumbing as other LLM adapters. | | `script` | **Requires** `command`. AFK-only (`bee run`); `bee chat` is LLM-only. `params` ignored. `prompt_template` optional. | Adapter drivers and flag mapping live in [architecture overview](../architecture/overview.md) §1. Machine-local credentials stay in `~/.config/paseka//adapters/*.yaml`. Script bee example: ```yaml # .paseka/bees/oracle-guard.yaml role: oracle-guard adapter: script command: ./scripts/oracle-guard.sh run_summary: disabled subscribes: - type: MUTATION kind: code.proposal.isolated dispatch: direct publishes: - type: VERIFICATION kind: verification.success - type: VERIFICATION kind: verification.failed ``` Script process env (in addition to `command` variable substitution): `PASEKA_TRACE_ID`, `PASEKA_AGENT_ID`, `PASEKA_TASK_ID`, `PASEKA_WORKSPACE`, `PASEKA_COLONY_ROOT`, `PASEKA_RUN_DIR`, `PASEKA_BEE`, `PASEKA_EVENT_LOG`, `PASEKA_RESULT_FILE`, `PASEKA_PROMPT_FILE`. Domain events still go through `paseka event emit --stdin`. --- ## 5. `params` Mapped by `RunParamsFromBee` (`internal/colony/params.go`). Defaults: `trust: true`, `force: true`. | Key | Type | Used by | | --- | ---- | ------- | | `model` | string | cursor, pi, claude | | `output_format` | string | cursor (`stream-json`, …); pi maps to `--mode` | | `trust` | bool | cursor | | `force` | bool | cursor | | `plan` | bool | cursor (`--plan`); pi (`--plan`) | | `binary` | string | override CLI binary name | | `provider` | string | pi | | `thinking` | string | pi | When `command` is set, these params are **not** turned into CLI flags; runtime logs a warning if both `command` and `params` are present. `adapter` still selects result parsing, session PTY, and home-config credential injection. --- ## 6. `command` and `post_exec` Both accept a shell-like string or a YAML list of strings (`colony.Command`). String form is split into argv without invoking a shell (quotes supported; unclosed quotes error). ```yaml command: agent -p --trust --workspace $WORKSPACE $PROMPT # or command: ["agent", "-p", "--model", "composer-2.5", "$PROMPT"] ``` ```yaml post_exec: notify.sh --bee builder --status ok --summary "$RESULT" # or post_exec: ["curl", "-fsS", "-d", "@$META", "https://hooks.example.com/paseka"] ``` ### Variable substitution Supports `$NAME` and `${NAME}`: | Variable | When set | Value | | -------- | -------- | ----- | | `$PROMPT` / `${PROMPT}` | dispatch + post_exec | rendered user/task prompt; for `cursor`, includes `system_template` when set (newline-separated) | | `$SYSTEM_PROMPT` / `${SYSTEM_PROMPT}` | dispatch + post_exec | rendered system prompt (still written to `system.txt`; not duplicated in Cursor positional when using default adapter mapping) | | `$SYSTEM_FILE` / `${SYSTEM_FILE}` | dispatch + post_exec | path to `system.txt` | | `$CURSOR_PLUGIN` / `${CURSOR_PLUGIN}` | dispatch + chat | **deprecated** — no longer materialized; `$PROMPT` carries merged system+task for Cursor | | `$WORKSPACE` / `${WORKSPACE}` | dispatch + post_exec | agent working directory | | `$TRACE_ID` / `${TRACE_ID}` | dispatch + post_exec | current flight trail | | `$AGENT_ID` / `${AGENT_ID}` | dispatch + post_exec | this invocation id | | `$TASK_ID` / `${TASK_ID}` | dispatch + post_exec | task id when dispatched from ledger | | `$COLONY_ROOT` / `${COLONY_ROOT}` | dispatch + post_exec | git repo root | | `$RUN_DIR` / `${RUN_DIR}` | dispatch + post_exec | `.paseka/runs///` | | `$RESULT_FILE` / `${RESULT_FILE}` | dispatch + post_exec | path to `summary.md` | | `$RESULT` / `${RESULT}` | post_exec only | human-readable run summary text | | `$META` / `${META}` | post_exec only | path to `meta.json` | --- ## 7. Sector and worktree - **`sector`** — default named path from `colony.yaml` `sectors`. Effective sector = task sector if set, else bee default (`EffectiveSector`). Workspace becomes `colonyRoot/` or worktree + sector path when `worktree: true`. - **`worktree: true`** — mutations under `.paseka/worktrees//`; audit I/O stays in `.paseka/runs/`. - **`worktree: false`** — adapter cwd is colony root (+ sector). Used for hivewright / main-guard root proposals. ### Worktree ↔ proposal kind invariants (hard) Auto-synthesis and `paseka doctor` enforce matching `worktree` and declared publish/subscribe kinds: | Bee `worktree` | Declares publish | Auto-publish kind | Else | | -------------- | ---------------- | ----------------- | ---- | | `true` | `isolated` or alias `code.proposal` | `code.proposal.isolated` | Skip auto mutation + warn | | `false` | `code.proposal.root` | `code.proposal.root` | Skip + warn | | `true` | only `root` | — | Skip + **doctor error** | | `false` | only `isolated` / alias | — | Skip + **doctor error** | Subscriber mismatches (`guard` with `worktree: false`, `main-guard` with `worktree: true`) are **doctor errors**. Bare `code.proposal` alias use is a **doctor warning**. `review: final` on a task whose bee publishes `code.proposal.root` is rejected at `task.plan` load. Fail closed: empty / missing `publishes` must **not** auto-publish mutations. Colony sector definitions remain in [architecture overview](../architecture/overview.md). --- ## 8. `run_summary` Controls runtime handling of `INSIGHT/run.summary`: | Value | Behavior | | ----- | -------- | | `auto` (default / empty) | Runtime may synthesize a summary when missing and policy allows | | `required` | Run fails if no summary event is present after the adapter exits | | `disabled` | No synthesis; useful for script / oracle bees | Invalid values fail bee load. See also [bee routing](../reference/bee-routing.md) §5 and [insight kinds](../reference/insight-kinds.md). --- ## 9. `completion_contract` Hard requirements checked against `events.ndjson` after the adapter exits. Violation → run **failed** even if the process exit code was zero. ```yaml completion_contract: required: - type: VERIFICATION kind_one_of: - verification.success - verification.failed count: 1 # default 1; must match exactly that count among allowed kinds ``` | Field | Meaning | | ----- | ------- | | `type` | Domain event type (`SIGNAL`, `INSIGHT`, `MUTATION`, `VERIFICATION`) | | `kind_one_of` | Allowed `payload.kind` values (required, non-empty) | | `count` | Exact match count among those kinds (default `1`) | Narrative INSIGHTs do not satisfy contracts unless listed. Full routing semantics: [bee routing](../reference/bee-routing.md) §6. --- ## 10. Routing fields (`subscribes` / `publishes`) Documented in [bee routing](../reference/bee-routing.md). Summary: - `subscribes[].dispatch`: `task` (task-ledger) or `direct` (reactor runs the bee on the event). - Empty `subscribes` → any `task.ready` dispatch allowed. - `publishes` is advisory in MVP. - Declaring `VERIFICATION/task.completed` marks the AFK commit gate: when another bee explicitly publishes an **isolated** `MUTATION/code.proposal` with a diff, runtime defers auto-complete until that commit-gate bee emits `task.completed`. Root proposals do not open this defer. - Declaring `MUTATION/code.proposal.isolated` (or alias `code.proposal`) on the dispatched bee (typically builder) is what opens the isolated defer path when a commit-gate publisher exists in the colony. - Declaring `MUTATION/code.proposal.root` on a `worktree: false` bee (typically hivewright) publishes from colony root; `main-guard` reviews on the same disk. --- ## 11. Prompt template resolution Precedence (highest wins), from [prompt templates](prompt-templates.md): 1. Inline `prompt:` / CLI `--prompt` 2. `bees/.local.yaml` → `prompt_template` 3. `bees/.yaml` → `prompt_template` 4. `colony.yaml` → `defaults.prompt_template` Do not store prompts in `~/.config/paseka/`. --- # Source: guide/prompt-templates.md --- # Prompt templates Paseka renders bee prompts from version-controlled Markdown files under `.paseka/prompts/`. Templates use Go [`text/template`](https://pkg.go.dev/text/template) syntax. At dispatch time the runtime fills in context variables, writes the result to `.paseka/runs///prompt.txt` (and optionally `system.txt`), and passes rendered strings to the adapter. Implementation: [`internal/prompts`](../../internal/prompts/prompts.go). --- ## 1. Directory layout ``` .paseka/prompts/ ├── _partials/ # shared snippets (not used as top-level bee templates) │ ├── emit-howto.md │ ├── emit-insight.md │ ├── emit-signal.md │ ├── emit-verification.md │ └── emit-task-completed.md ├── default.md # colony-wide fallback ├── scout.md └── builder.md ``` | Path | Role | | ---- | ---- | | `.paseka/prompts/*.md` | Bee prompt templates | | `.paseka/prompts/_partials/*.md` | Reusable partials included via `{{template "name" .}}` | | `~/.config/paseka//` | **No prompts** — secrets and machine-local state only | Prompts belong in the git repo so the colony shares the same instructions across machines. --- ## 2. Linking a template to a bee Each bee references template files (relative to `.paseka/prompts/`) in its config: ```yaml # .paseka/bees/scout.yaml role: scout adapter: pi system_template: scout-system.md # role / standing instructions (optional) prompt_template: scout.md # user/task turn worktree: true ``` | Field | Artifact | Role | | ----- | -------- | ---- | | `system_template` (optional) | `system.txt` | Identity and standing instructions — injected by the adapter, not shown as the first chat turn | | `prompt_template` | `prompt.txt` | User/task message for AFK runs; optional kickoff for interactive chat | Colony-wide fallbacks when a bee omits a field: ```yaml # .paseka/colony.yaml defaults: prompt_template: default.md system_template: default-system.md # optional ``` When `system_template` is unset, behavior matches the previous single-template model (full prompt as positional argv only). --- ## 3. Supported variables The runtime passes a single context object (`prompts.Context`) to every template. In templates, reference fields as `{{.FieldName}}`. | Variable | Type | Description | | -------- | ---- | ----------- | | `{{.Bee}}` | `string` | Bee role from `bees/.yaml` (e.g. `scout`, `builder`). | | `{{.TraceID}}` | `string` | Flight trail id for the current task chain. From `--trace` or generated by runtime. | | `{{.TraceTitle}}` | `string` | Resolved human Flight Trail title. From latest `INSIGHT/trace.title`, then `feature.requested` title, then first task title; empty when unresolved. | | `{{.AgentID}}` | `string` | Unique id for this agent invocation. Generated per run. | | `{{.TaskID}}` | `string` | Optional task id within the trace. From `DispatchRequest.TaskID` when dispatching a queued subtask. | | `{{.ColonyRoot}}` | `string` | Absolute path to the git repository root. | | `{{.Workspace}}` | `string` | Absolute cwd for the adapter: colony root, or `.paseka/worktrees//` when `worktree: true`. | | `{{.Task}}` | `string` | Task body (nectar). From CLI `--body`, Queen Console session launch, or bus event payload. | | `{{.Intent}}` | `string` | Normalized task intent for partial routing within the bee's vocabulary. Empty or unknown caller input becomes the bee's default intent. | | `{{.IntentRaw}}` | `string` | Caller-supplied intent before normalization (CLI `--intent`, task ledger, or bus payload). | | `{{.Insights}}` | `[]string` | Narrative INSIGHT strings projected from prior runs on the trace. See [insight kinds](../reference/insight-kinds.md). | | `{{.ResultFile}}` | `string` | Absolute path to the human-readable `summary.md` log for this run under `.paseka/runs///`. | | `{{.Interactive}}` | `bool` | `true` for interactive `paseka bee chat` sessions; `false` for AFK dispatch. | | `{{.IsLastWorkTask}}` | `bool` | `true` at AFK ledger task dispatch when the current task is the sole incomplete non-final work task; `false` for chat, ad-hoc `bee run`, and all other paths. Gates must-emit `trace.summary` guidance in emit partials. | | `{{.Adapter}}` | `string` | Resolved adapter name (`cursor`, `pi`, `claude`, `script`). | ### Field sources (MVP) | Variable | Set by | | -------- | ------ | | `Bee`, `TraceID`, `TraceTitle`, `AgentID`, `TaskID`, `ColonyRoot`, `Workspace`, `Task`, `Intent`, `IntentRaw`, `Insights`, `ResultFile`, `Interactive`, `IsLastWorkTask`, `Adapter` | `internal/runtime.Dispatcher` at dispatch time; `Interactive` is `true` in `internal/sessions` for chat | | `IsLastWorkTask` | `taskledger.IsLastWorkTask` at AFK ledger task dispatch (`DispatchModeTask`) only; always `false` for CLI `bee run`, direct signal dispatch, and chat | | `TraceTitle` | `runs.ResolveTraceTitle` from prior trace events and task projections | | `Task` | `paseka bee run --body` (required unless using inline prompt) | | `Intent` / `IntentRaw` | `paseka bee run --intent`, `paseka task create --intent`, or `intent` on `task.plan` / `task.ready` payloads | | `Insights` | Runtime projection from prior narrative `INSIGHT` events on the trace, merged with any manual `DispatchRequest.Insights` | | `ResultFile` | Computed from colony root + trace + agent ids | Variables **not** available in templates today: - Bee adapter params (`model`, `trust`, etc.) — configured in `bees/*.yaml`, not exposed to templates. - Arbitrary bus event fields — only `Task`, `Intent`, and `Insights` are surfaced in MVP. Bus event publishing is instructed through emit partials: `emit-howto` (safe CLI mechanics for all bees) plus type-scoped partials (`emit-insight`, `emit-signal`, `emit-verification`, `emit-task-completed`) included only by bees that may publish those types. --- ## 4. Template syntax Paseka uses standard Go `text/template` with no custom functions. ### Interpolation ```markdown Colony: {{.ColonyRoot}} Flight trail: {{.TraceID}} Agent: {{.AgentID}} ``` ### Conditionals ```markdown {{if .Task}} ## Task {{.Task}} {{else}} No task body provided. {{end}} ``` ### Loops ```markdown ## Prior discoveries {{range .Insights}}- {{.}} {{end}} ``` When `Insights` is empty, the range produces no lines. ### Partials Partials live in `.paseka/prompts/_partials/`. The file name without `.md` is the template name: ``` _partials/emit-howto.md → {{template "emit-howto" .}} _partials/emit-insight.md → {{template "emit-insight" .}} _partials/emit-signal.md → {{template "emit-signal" .}} _partials/emit-verification.md → {{template "emit-verification" .}} _partials/emit-task-completed.md → {{template "emit-task-completed" .}} _partials/builder-intent-feature.md → {{template "builder-intent-feature" .}} ``` Builder Bee uses intent partials for mission-specific guidance while keeping one stable role prompt. The top-level `builder.md` routes by `{{.Intent}}` and falls back to `builder-intent-general`. ### Per-bee intent vocabulary Each bee may define an intent vocabulary used for `{{.Intent}}` normalization and Queen Console intent pickers: 1. **Explicit** — `intents:` and optional `default_intent:` in `bees/.yaml` (see [bee config](bee-config.md)). 2. **Discovered** — when `intents` is omitted, runtime scans `_partials/-intent-*.md` (e.g. `builder-intent-feature.md` → `feature`, `drone-intent-grilling.md` → `grilling`). At dispatch, empty or unknown caller input normalizes to the bee's default intent (`general` when present, otherwise the first discovered intent). The raw requested value remains in `{{.IntentRaw}}` when it differs from `{{.Intent}}`. Bees without YAML intents and without `-intent-*` partials have no intent vocabulary; `{{.Intent}}` stays empty unless the caller passes a recognized value. Include only the emit partials your bee role may publish: ```markdown {{template "emit-howto" .}} {{template "emit-insight" .}} ``` Partials are loaded before the main template and can use the same variables (`{{.TraceID}}`, etc.). --- ## 5. Override precedence When resolving which template to render, the **first non-empty** source wins: | Priority | Source | Example | | -------- | ------ | ------- | | 1 (highest) | Inline prompt | `paseka bee run builder --prompt "Fix {{.Task}}"` | | 2 | Bee local overlay | `.paseka/bees/builder.local.yaml` → `prompt_template` / `system_template` | | 3 | Bee config | `.paseka/bees/builder.yaml` → `prompt_template` / `system_template` | | 4 (lowest) | Colony default | `.paseka/colony.yaml` → `defaults.prompt_template` / `defaults.system_template` | `*.local.yaml` files are gitignored — use them for machine- or developer-specific template overrides without committing. Inline prompts are still parsed as `text/template` bodies (partials are available). --- ## 6. Rendering pipeline ``` bee config + CLI flags │ ▼ Resolve template (precedence §5) │ ▼ Load partials from _partials/ │ ▼ Execute text/template with Context (§3) │ ├─► Write system.txt (when system_template configured) │ ▼ Write .paseka/runs///prompt.txt │ ▼ Adapter injects system context (per adapter) and runs external agent │ ▼ Runtime normalizes summary, writes log artifact, may auto-publish `INSIGHT/run.summary` ``` Bus events are published separately through `paseka event emit --stdin` (live by default; `--defer` for end-of-run handoffs) as described in the emit partials. Each bee includes `emit-howto` plus only the type partials it may publish. Runtime may also synthesize `INSIGHT/run.summary` after a successful AFK run when the bee policy allows. --- ## 7. Examples ### Builder bee ```markdown # .paseka/prompts/builder.md You are Builder Bee. Implement the task in the workspace. Intent: {{.Intent}} ## Task {{.Task}} {{if eq .Intent "bugfix"}} {{template "builder-intent-bugfix" .}} {{else}} {{template "builder-intent-general" .}} {{end}} {{template "emit-howto" .}} {{template "emit-insight" .}} ``` Known builder intents (discovered from `builder-intent-*` partials): `general` (default), `feature`, `bugfix`, `test-fix`, `refactor`. Drone uses `drone-intent-*` partials (`general`, `grilling`, `breakdown`) and routes on `{{.IntentRaw}}` in its template; grilling includes `drone-emit-grilling`, breakdown includes `drone-emit-breakdown`. Scout uses `scout-intent-*` partials (`intake` default via bee `default_intent`, `survey` manual); intake includes `scout-emit-intake`. ### Scout bee with bus-event partial ```markdown # .paseka/prompts/scout.md You are Scout Bee. Your job is problem discovery, not implementation. Colony: {{.ColonyRoot}} Flight trail: {{.TraceID}} Intent: {{.Intent}} ## Task {{.Task}} ## Prior discoveries {{range .Insights}}- {{.}} {{end}} ## Mission guidance {{if eq .Intent "intake"}} {{template "scout-intent-intake" .}} {{template "scout-emit-intake" .}} {{else}} {{template "scout-intent-survey" .}} {{end}} {{template "emit-howto" .}} {{template "emit-insight" .}} {{template "emit-signal" .}} ``` ### Inline one-shot prompt ```bash paseka bee run builder --prompt "Hotfix only: {{.Task}}" --body "null pointer in auth" ``` Renders to: `Hotfix only: null pointer in auth` ### CLI with task and trace ```bash paseka bee run builder --body "add OAuth login" --trace trace-auth-01 ``` `{{.Task}}` and `{{.TraceID}}` are filled; other fields come from runtime defaults. --- ## 8. Constraints and validation | Rule | Behavior | | ---- | -------- | | Template path | Must be relative to `.paseka/prompts/` | | Path traversal | `..` and absolute paths are rejected | | Missing template | Dispatch fails with a clear error | | Missing partial | Dispatch fails at parse time | | Empty template chain | Error: `prompts: no template configured` | --- ## 9. Shared partials Core partials shipped by `paseka init` under `.paseka/prompts/_partials/`: | Partial | Role | | ------- | ---- | | `emit-howto` | Safe CLI publish contract via `paseka event emit --stdin` (live default, `--defer` for handoffs; no type enumeration) | | `emit-insight` | `INSIGHT` kinds for narrative and prompt memory (`run.summary`, `review.note`, `context.note`, `human.feedback`, `task.plan`) | | `emit-signal` | `SIGNAL` kinds (`task.ready`) | | `scout-emit-intake` | `SIGNAL/feature.classified`, `INSIGHT/task.plan`, `SIGNAL/task.ready` (Scout `intake` intent only) | | `drone-emit-grilling` | `SIGNAL/spec.ready` + optional `context.note` (Drone `grilling` intent only) | | `drone-emit-breakdown` | `INSIGHT/task.plan`, `SIGNAL/task.ready`, optional `context.note` (Drone `breakdown` intent only) | | `emit-verification` | Review-gate `VERIFICATION` kinds (`verification.success`, `verification.failed`) | | `emit-task-completed` | Commit-gate `VERIFICATION/task.completed` (receiver only) | | `cursor-interactive-kickoff` | Brief greet-and-wait footer for interactive Cursor chat (`hivewright-task`, `drone-task`) | Bees include only the type partials they may publish. For example: | Bee | Emit partials | | --- | ------------- | | `builder` | `emit-howto`, `emit-insight` | | `scout` | `emit-howto`, `emit-insight`, `emit-signal`; on `intake` also `scout-emit-intake` | | `drone` | `emit-howto`; on `grilling` also `drone-emit-grilling`; on `breakdown` also `drone-emit-breakdown` | | `guard` | `emit-howto`, `emit-verification`, `emit-insight` | | `main-guard` | `emit-howto`, `emit-verification`, `emit-insight` | | `receiver` | `emit-howto`, `emit-task-completed` | | `hivewright` | `emit-howto`, `emit-insight` | `MUTATION` is not taught in prompts — runtime auto-publishes `code.proposal.isolated` or `code.proposal.root` from **baseline-attributed workspace diffs** (tracked changes in the adapter cwd; review truth is working-tree `git diff`, not staged-only). Guard and main-guard prompts instruct disk review via `git diff`. See [insight kinds](../reference/insight-kinds.md) for the full INSIGHT taxonomy and prompt-memory rules. ```bash paseka event emit --stdin <<'EOF' {"traceId":"","agentId":"","type":"INSIGHT","payload":{"kind":"task.plan","tasks":[{"taskId":"task-1","title":"..."}]}} EOF ``` Use `{{.TraceID}}` and `{{.AgentID}}` inside partials so examples match the current run. See [task ledger](../reference/task-ledger.md). --- ## 10. Related docs - [architecture overview](../architecture/overview.md) — colony layout, adapter contract, runs/worktrees - [bee config](bee-config.md) — bee role YAML (`prompt_template` and other fields) - [task ledger](../reference/task-ledger.md) — task queue protocol and lifecycle - [glossary](../idea/glossary.md) — bee language vs technical terms (`TraceID` / Flight Trail, `Task` / Nectar) - Agent run file protocol — `request.json`, `summary.md`, `events.ndjson` under `.paseka/runs/` --- # Source: reference/bee-routing.md --- # Bee event routing Declarative `subscribes` and `publishes` in `.paseka/bees/.yaml` describe how bees participate in choreographed bus flows without giving each bee its own NATS consumer. Implementation: [`internal/colony/routing.go`](../../internal/colony/routing.go), [`internal/runtime/reactor.go`](../../internal/runtime/reactor.go). For a static graph of how these rules connect in your colony config, see [spec 007: Colony EDA Topology](../specs/007-colony-eda-topology.md) (Queen Console **Topology** tab and `paseka colony topology`) — observability only; routing semantics remain in this doc. --- ## 1. Principles - **One reactor** — `paseka run` keeps a single JetStream consumer (`Reactor`) that applies routing rules from all bee configs. - **Task ledger stays canonical** — `task.plan` → `task.ready` → `task.completed` still drives dependency-aware work queues. - **Hybrid dispatch** — some subscriptions trigger task-ledger dispatches; others trigger **direct** bee runs on domain events (e.g. code review). - **Advisory publishes** — `publishes` documents expected output; runtime logs warnings for undeclared domain events but does not block them (MVP). - **Role vs intent** — routing selects the bee role (`builder`, `guard`, …). Optional `intent` on tasks tunes prompt guidance inside a role without creating separate bees. --- ## 2. Config shape Each rule matches bus events by **top-level `type`** (`SIGNAL`, `INSIGHT`, `MUTATION`, `VERIFICATION`) and optional **`payload.kind`**. ```yaml # .paseka/bees/builder.yaml subscribes: - type: SIGNAL kind: task.ready dispatch: task - type: VERIFICATION kind: verification.failed dispatch: direct publishes: - type: MUTATION kind: code.proposal.isolated - type: VERIFICATION kind: task.completed ``` ```yaml # .paseka/bees/guard.yaml subscribes: - type: MUTATION kind: code.proposal.isolated dispatch: direct publishes: - type: VERIFICATION kind: verification.success - type: VERIFICATION kind: verification.failed ``` ```yaml # .paseka/bees/hivewright.yaml — root proposals on colony checkout worktree: false publishes: - type: MUTATION kind: code.proposal.root ``` ```yaml # .paseka/bees/main-guard.yaml — reviews root proposals on colony root worktree: false subscribes: - type: MUTATION kind: code.proposal.root dispatch: direct publishes: - type: VERIFICATION kind: verification.success - type: VERIFICATION kind: verification.failed ``` ### Fields | Field | Meaning | | ----- | ------- | | `type` | `protocol.EventType` published on the bus | | `kind` | `payload.kind` inside the event JSON (optional wildcard when omitted) | | `dispatch` | `task` — capability for task-ledger dispatches; `direct` — reactor runs this bee when the event arrives | If `dispatch` is omitted: - `task.*` kinds default to `task` - other kinds default to `direct` ### Backward compatibility Bees **without** `subscribes` behave as before: any `task.ready` dispatch is allowed. --- ## 3. NATS subject mapping Subjects follow [`internal/bus/subject.go`](../../internal/bus/subject.go): ```text .events.[.] ``` Examples: - `paseka.demo.events.SIGNAL.task.ready` - `paseka.demo.events.MUTATION.code.proposal.isolated` - `paseka.demo.events.MUTATION.code.proposal.root` - `paseka.demo.events.VERIFICATION.verification.failed` Routing matches on parsed event `type` + `payload.kind`, not on raw subject strings. ### Code proposal kinds and alias | `payload.kind` | Workspace | Typical publisher | Typical reviewer | | -------------- | --------- | ----------------- | ---------------- | | `code.proposal.isolated` | `.paseka/worktrees//` (+ sector) | `builder` (`worktree: true`) | `guard` (`worktree: true`) | | `code.proposal.root` | Colony root (+ sector) | `hivewright` (`worktree: false`) | `main-guard` (`worktree: false`) | | `code.proposal` (alias) | Same as isolated | Legacy YAML | Matches isolated subscribers | - Bare `code.proposal` in bee YAML is accepted as an **alias of `code.proposal.isolated`**. - Runtime **normalizes alias → `code.proposal.isolated` on auto-publish write** (never leaves bare alias on the wire). - Subscription matching: a subscriber of `code.proposal` **or** `code.proposal.isolated` matches isolated events. A subscriber of only `code.proposal.root` does **not** match isolated (or alias). - `paseka doctor` warns when bare alias is still in use; prefer explicit kinds. --- ## 4. Runtime flow ```mermaid flowchart LR busEvent[BusEvent] --> reactor[Reactor] reactor --> ledger[TaskLedger] ledger --> taskDispatch[TaskDispatch] reactor --> directDispatch[DirectDispatch] taskDispatch --> dispatcher[Dispatcher] directDispatch --> dispatcher dispatcher --> adapter[AdapterRun] adapter --> publish[PublishOutcome] ``` ### Task path 1. `INSIGHT/task.plan` registers tasks in the ledger. 2. `SIGNAL/task.ready` (or dependency unlock after `task.completed`) marks tasks ready. 3. Reactor dispatches the bee named in `task.Bee` when set; otherwise `defaults.default_bee` from `colony.yaml` (platform fallback `builder`). Dispatch runs **only if** that bee subscribes to `task.ready` (or has no `subscribes` block). 4. On successful run with `review: none`: - If the run already emitted `VERIFICATION/task.completed`, apply it. - Else if a colony bee explicitly declares `publishes: VERIFICATION/task.completed` **and** this run opened an **isolated** `code.proposal` (emitted `code.proposal.isolated` / alias, or non-empty diff with explicit isolated publish on the dispatched bee), set `waiting_review` and wait for the commit-gate publisher (typically receiver). - Else runtime publishes `VERIFICATION/task.completed` (fallback for scout, no-diff runs, colonies without a commit-gate publisher). **AFK defer scope:** only **isolated** proposals (`code.proposal.isolated` and alias) open the receiver commit-gate defer. `code.proposal.root` does **not** defer AFK completion — root human review uses the soft-ack path when `review: required` (see [task ledger](task-ledger.md)). ### Direct path When a domain event arrives, reactor finds all bees with `dispatch: direct` subscriptions and runs them with context derived from the event payload. **Workspace affinity:** isolated proposals dispatch reviewers into the trace worktree (reuse existing dirty tree); root proposals dispatch reviewers to colony root (never ensure worktree). | Event | Typical bee | Workspace | Task context | | ----- | ----------- | --------- | ------------ | | `MUTATION/code.proposal.isolated` (+ alias) | `guard` | Trace worktree (+ sector) | diff + summary; review truth is disk | | `MUTATION/code.proposal.root` | `main-guard` | Colony root (+ sector) | diff + summary; review truth is disk | | `VERIFICATION/verification.failed` | `builder` | Per bee/task rules | failure summary for fix-up | | `VERIFICATION/verification.success` | `receiver` | — | approval summary for commit gate | | `SIGNAL/feature.requested` (colony) | `scout` | Colony root | title/body from payload; bee `default_intent` (e.g. `intake`) | | `SIGNAL/spec.ready` (colony) | `drone` (when subscribed) | Colony root | body/summary/ref from payload | **Platform SIGNAL kinds** (`task.ready`, `task.status`, `energy.*`, `session.invite`, `beekeeper.ready`) are **not** valid direct-dispatch targets — they use the task ledger, energy subsystem, or Human Gateway. Runtime refuses them even if a bee misconfigures `dispatch: direct`. Duplicate runs are suppressed per `traceId + taskId + bee + type + kind` when `payload.taskId` is set, except for rework-cycle gates (`MUTATION/code.proposal.isolated`, `MUTATION/code.proposal.root`, `code.proposal` alias, `VERIFICATION/verification.failed`): those key by event identity so each publisher→reviewer pass can run again on the same task. Direct dispatch also skips when the publishing run's bee role matches the subscriber (prevents receiver self-loops if it mistakenly re-emits `verification.success`). --- ## 5. Advisory publishes After an adapter run, `Dispatcher.publishRunOutcome` compares emitted domain events against `bee.publishes`: - **Declared** — no action - **Undeclared** — log warning + append to `RunResult.Warnings` - Events are still published (no enforcement in MVP) Auto-generated `MUTATION/code.proposal.isolated` or `code.proposal.root` from workspace diffs is published **only** when the bee declares the matching kind in `publishes` and `worktree` matches the kind (see [bee config](../guide/bee-config.md) § worktree invariants). Runtime captures a **baseline-attributed** tracked diff (MVP: tracked changes only). Reviewer bees like `guard` run `git diff` for artifacts but do not emit a bus mutation unless they declare one. Runtime may also auto-publish `INSIGHT/run.summary` after successful AFK runs when the bee `run_summary` policy allows (`auto` by default). Set `run_summary: disabled` to skip synthesis or `run_summary: required` to fail the run when no summary event is present. ```yaml # .paseka/bees/builder.yaml run_summary: auto # auto | required | disabled ``` --- ## 6. Completion contracts Bees may declare required post-run domain events via `completion_contract` in `bees/.yaml`. Runtime validates `events.ndjson` after the adapter exits and marks the run **failed** when the contract is violated, even if the process completed successfully. Example for `guard`: ```yaml completion_contract: required: - type: VERIFICATION kind_one_of: - verification.success - verification.failed count: 1 ``` Narrative `INSIGHT` events are optional and do not satisfy completion contracts. See [insight kinds](insight-kinds.md). --- ## 7. Colony `auto_invites` (Human Gateway) Bee `subscribes` imply `Adapter.Run()` dispatch. **Auto-invite** is separate colony choreography: when a bus event matches, `paseka run` publishes a pending `session.invite` for Beekeeper accept/reject. **`payload.decision` vs routing:** On colony events (e.g. `feature.classified`), `payload.decision` is a **classification tag** on the branch (`grill`, `plan`, …). Colony rules may match it via `auto_invites.match.decision`. That is distinct from (1) bee **`subscribes`** dispatch (`type` + `payload.kind` → AFK run) and (2) glossary **Flight Route** — the NATS subject (`events.[.]`, §3). See [specs/005-feature-ideation-flow.md](../specs/005-feature-ideation-flow.md). Rules live in **`.paseka/colony.yaml`** (not bee YAML). Implementation: [`internal/colony/invite_rules.go`](../../internal/colony/invite_rules.go), [`internal/invites/auto_invite.go`](../../internal/invites/auto_invite.go), [`internal/runtime/invite_publisher.go`](../../internal/runtime/invite_publisher.go). ```yaml auto_invites: - when: type: SIGNAL kind: feature.classified match: decision: grill invite: bee: { default: drone } intent: { default: grilling } task: from_trace_kind: feature.requested from_trace_field: title prefix: "Grill feature: " fallback_from: rationale default: Grill feature status: pending done_when: when: { type: SIGNAL, kind: spec.ready } require_file: { from: ref } set_artifact_ref: { from: ref } dedupe: [bee, intent] - when: type: SIGNAL kind: spec.ready invite: bee: { default: drone } intent: { default: breakdown } artifactRef: { from: ref } task: { from: ref, prefix: "Break down ", default: Break down spec } status: pending dedupe: [intent, artifactRef] ``` | Field | Meaning | | ----- | ------- | | `when` | Same as bee `subscribes`: `type` + optional `kind` | | `match` | AND equality on top-level payload string fields | | `invite.*.from` / `default` | Copy string from trigger payload or fallback | | `invite.task.from_trace_*` | Latest prior trace event with that `kind`; read field | | `invite.task.fallback_from` | Field on trigger payload if trace lookup fails | | `invite.done_when` | Optional completion contract persisted on the invite (see §8) | | `dedupe` | Skip when a **pending** invite on the trace matches those invite fields | `paseka init` seeds the grill and breakdown rules above (feature ideation reference). With **empty** `auto_invites`, no auto-invite runs. See [specs/005-feature-ideation-flow.md](../specs/005-feature-ideation-flow.md) and [specs/006-human-gateway-invites.md](../specs/006-human-gateway-invites.md). --- ## 8. Invite `done_when` (completion contract) An invite is a **work contract**: required `task` (input) plus optional `done_when` (expected result). When a bus event matches a persisted invite's `done_when`, `paseka run` updates that invite by `inviteId` to `completed` (file exists at `ref`) or `incomplete` (missing file). Implementation: [`internal/invites/completion.go`](../../internal/invites/completion.go), [`internal/runtime/invite_completer.go`](../../internal/runtime/invite_completer.go). ```yaml invite: task: { ... } done_when: when: { type: SIGNAL, kind: spec.ready } match: { optional: equality } require_file: { from: ref } set_artifact_ref: { from: ref } ``` | Field | Meaning | | ----- | ------- | | `done_when.when` | Same as `auto_invites.when`: `type` + optional `kind` | | `done_when.match` | Optional AND equality on trigger payload string fields | | `done_when.require_file.from` | Payload field with repo-relative path; file must exist under colony root or trace worktree | | `done_when.set_artifact_ref.from` | Copy payload field into invite `artifactRef` on success | Only **accepted** or **incomplete** invites with a `doneWhen` on the same trace are evaluated. Without `done_when`, bus-driven completion does not run (session-end `incomplete` still applies). --- ## 9. Related docs - [specs/007-colony-eda-topology.md](../specs/007-colony-eda-topology.md) — config-derived EDA graph (Console Topology tab, `paseka colony topology`) - [task ledger](task-ledger.md) — task lifecycle events - [architecture overview](../architecture/overview.md) — colony layout and adapters - [bee config](../guide/bee-config.md) — full bee YAML schema (`role`, `adapter`, contracts, …) - [insight kinds](insight-kinds.md) — INSIGHT taxonomy and prompt memory projection - [specs/006-human-gateway-invites.md](../specs/006-human-gateway-invites.md) — invite lifecycle, CLI/Console, energy --- # Source: reference/insight-kinds.md --- # INSIGHT kinds and prompt memory This document defines the `INSIGHT` event taxonomy, how narrative insights differ from workflow `VERIFICATION` events, and how runtime projects prior insights into `{{.Insights}}` for subsequent bees. --- ## Role split | Event type | Purpose | Drives routing? | | ---------- | ------- | --------------- | | `VERIFICATION` | Gate outcomes and verified domain facts | yes | | `INSIGHT` | Narrative context, audit trail, dashboard timeline | no | | `MUTATION` | Code change proposals | yes (via direct dispatch) | | `SIGNAL` | Triggers and task-ready notifications | yes | **Rule of thumb:** publish `VERIFICATION` when the colony must decide what happens next; publish `INSIGHT` when you want downstream bees (or humans) to understand what happened. --- ## INSIGHT kinds ### Operational (not projected into prompts) | `payload.kind` | Type | Description | | -------------- | ---- | ----------- | | `task.plan` | `INSIGHT` | Scout/planner task breakdown for the task ledger | | `trace.title` | `INSIGHT` | Human-readable Flight Trail name for Console and `{{.TraceTitle}}` | | `trace.summary` | `INSIGHT` | Human-readable Flight Trail description for Console subtitle and merge commit body | `task.plan` is consumed by the Task Ledger and reactor. It is **not** auto-included in `{{.Insights}}` because it is structured queue data, not narrative memory. `trace.title` is trace identity metadata (last-write-wins). It is **not** projected into `{{.Insights}}`. Runtime and Console resolve the display title with fallbacks from `feature.requested` and task ledger titles. See [specs/011-trace-title.md](../specs/011-trace-title.md). `trace.summary` is operational trail metadata (last-write-wins). It is **not** projected into `{{.Insights}}` or dashboard Recent insights. Runtime resolves the latest `INSIGHT/trace.summary` by `createdAt`, then `seq`. Payload shape: `{ "kind": "trace.summary", "summary": "" }` (max 800 characters after trim). Bees on the sole incomplete non-final AFK work task receive must-emit guidance via `{{.IsLastWorkTask}}` in emit partials. See [specs/012-trace-summary.md](../specs/012-trace-summary.md). ### Narrative (projected into `{{.Insights}}`) | `payload.kind` | Required fields | Optional fields | Typical producer | | -------------- | --------------- | --------------- | ---------------- | | `run.summary` | `summary` | `taskId` | builder, scout; runtime may auto-synthesize after AFK runs | | `review.note` | `summary` | `taskId`, `severity` | guard | | `context.note` | `summary` | `taskId` | any bee | | `human.feedback` | `taskId`, `message` | — | beekeeper via CLI | ### Example payloads **`run.summary`** ```json { "traceId": "trace-auth-01", "agentId": "a1b2c3d4", "type": "INSIGHT", "payload": { "kind": "run.summary", "summary": "Implemented OAuth callback and added focused tests", "taskId": "task-1" } } ``` **`review.note`** ```json { "traceId": "trace-auth-01", "agentId": "e5f6a7b8", "type": "INSIGHT", "payload": { "kind": "review.note", "summary": "Token refresh path still lacks retry handling", "taskId": "task-1", "severity": "medium" } } ``` --- ## Prompt memory projection Before rendering a bee prompt, runtime: 1. Reads all `events.ndjson` files under `.paseka/runs//`. 2. Selects narrative `INSIGHT` kinds (`run.summary`, `review.note`, `context.note`, `human.feedback`). 3. Prefers insights scoped to the current `taskId`, then adds trace-scoped insights (no `taskId`). 4. Deduplicates, truncates long lines, and caps the list (default: 5 entries). 5. Merges with any manually supplied `Insights` from dispatch input. Projected strings appear in templates via: ```markdown ## Prior discoveries {{range .Insights}}- {{.}} {{end}} ``` --- ## MUTATION kinds (workflow) Code proposals use `MUTATION` with explicit workspace provenance. They drive direct dispatch to reviewers and set `proposalWorkspace` on the task ledger. | `payload.kind` | Workspace | Typical producer | Typical subscriber | | -------------- | --------- | ---------------- | ---------------- | | `code.proposal.isolated` | `.paseka/worktrees//` (+ sector) | `builder` (`worktree: true`) | `guard` (`worktree: true`) | | `code.proposal.root` | Colony root (+ sector) | `hivewright` (`worktree: false`) | `main-guard` (`worktree: false`) | | `code.proposal` (alias) | Same as isolated | Legacy YAML | Matches isolated subscribers | Bare `code.proposal` is normalized to `code.proposal.isolated` on auto-publish write. Payload may include `workspace`, `baseSha`, `worktreePath` (isolated), `sector`, `diff` / `ref`, `summary`, `taskId`. See [specs/008-code-proposal-workspaces.md](../specs/008-code-proposal-workspaces.md). --- ## VERIFICATION routing Workflow handoff remains `VERIFICATION`-driven: | Event | Typical subscriber | Handoff | | ----- | ------------------ | ------- | | `MUTATION/code.proposal.isolated` (+ alias) | `guard` | diff + summary; reviewer cwd = trace worktree | | `MUTATION/code.proposal.root` | `main-guard` | diff + summary; reviewer cwd = colony root | | `VERIFICATION/verification.failed` | `builder` | failure summary for fix-up | | `VERIFICATION/verification.success` | `receiver` (isolated defer) or ledger (root `review: required`) | approval / soft-gate advance | See [bee routing](bee-routing.md). --- ## Bee completion contracts Bees may declare required post-run events in `bees/.yaml`: ```yaml completion_contract: required: - type: VERIFICATION kind_one_of: - verification.success - verification.failed count: 1 ``` Runtime validates emitted domain events in `events.ndjson` after the adapter exits. A process-level success is downgraded to **failed** when the completion contract is violated or when `run_summary: required` is set and no `INSIGHT/run.summary` is present. The `guard` bee requires exactly one `VERIFICATION` gate decision per run. --- ## Related docs - [architecture overview](../architecture/overview.md) — adapter contract and runs layout - [prompt templates](../guide/prompt-templates.md) — template fields and partials - [task ledger](task-ledger.md) — `task.plan` lifecycle - [bee routing](bee-routing.md) — direct dispatch and advisory publishes --- # Source: guide/colony-layout.md --- # Colony layout and configuration Start here for where colony config lives, how `.paseka/` relates to machine-local state, and what `paseka init` creates. For adapters, run directories, worktrees, and package layout see [Architecture overview](../architecture/overview.md). ## 1. Colony-centric model | Concept | Location | Role | | ------- | -------- | ---- | | **Colony** | Git repo root | Source of truth for code, history, and shareable hive config | | **Apiary** | Developer machine | Hosts Hive Runtime, NATS, and local adapter credentials | | **Bee** | Config + runtime | A role (Scout, Guard, Builder…) bound to an **adapter** that drives an external agent | The runtime never owns LLM logic. It **orchestrates** external tools via **adapters** — the **Cursor Agent CLI** (`agent`), the **Pi CLI** (`pi`), **Claude Code**, and **script** commands — reads their output, and publishes results to the NATS bus as contract events. To run the apiary on a separate always-on host (containerized toolbelt + Queen Console, reuse an existing NATS), see [Homelab deployment](homelab-deployment.md). --- ## 2. Two-tier configuration ### Project-local: `.paseka/` (in repo) Version-controlled colony definition. Safe to commit; no secrets. ``` .paseka/ ├── colony.yaml # colony manifest: bees, routes, defaults ├── bees/ # per-bee adapter bindings and non-secret params │ ├── scout.yaml │ └── builder.yaml ├── prompts/ # prompt templates (committed); see §2.1 │ ├── _partials/ # shared snippets (JSON contract, tone, etc.) │ ├── scout.md │ └── builder.md ├── cues/ # Forage Cue ingress shortcuts (committed); see [cues](cues.md) │ ├── feature.yaml │ └── hotfix.yaml ├── .gitignore # ignores worktrees/, runs/, cache/, *.local.yaml ├── runs/ # gitignored — per-agent file IPC (architecture overview) │ └── / │ ├── / │ │ ├── prompt.txt │ │ ├── system.txt # optional — rendered system_template │ │ ├── summary.md │ │ ├── meta.json │ │ └── status.json │ └── tasks/ │ └── / │ ├── task.md │ └── runs.ndjson └── worktrees/ # gitignored — isolated mutation workspaces └── / ``` **`colony.yaml`** — colony identity, default branch, bee registry, optional **sectors** (module/subfolder workspace scopes), NATS subject prefixes (optional overrides), colony-wide defaults including per-trace honey reserve (`defaults.energy_budget`, default `12`), and optional **`auto_invites`** (HITL choreography that publishes `session.invite` when bus events match — see [bee routing](../reference/bee-routing.md)). ```yaml defaults: prompt_template: default.md system_template: default-system.md # optional colony-wide role context energy_budget: 12 default_bee: builder # task role when task.bee is omitted ``` Each `traceId` shares one **Honey Reserve** (`energyToken`): every adapter dispatch (`task.ready` and direct routing) consumes one token. When the reserve is empty, tasks move to `blocked` with summary `Honey reserve exhausted`. Beekeepers can top up via `paseka energy add --trace --amount `. Per-cue **initial** reserve overrides on fresh trails: [Forage Cues](cues.md) § Honey. Example sectors for monorepos or git-submodule layouts: ```yaml sectors: frontend: path: frontend backend-users: path: backend/users ``` A **sector** is a named path inside the colony. Tasks may optionally set `sector`; bees may declare a default `sector` in `bees/*.yaml`. Runtime resolves the adapter workspace as `colonyRoot/` or `.paseka/worktrees//` when `worktree: true`. The colony root remains the audit boundary for `.paseka/runs/`. **`bees/*.yaml`** — one file per role: binds the bee to an adapter, prompt template(s), optional `command` / `post_exec`, sector/worktree, and routing rules. Full schema, examples, and variable substitution: [bee config](bee-config.md). Event routing (`subscribes` / `publishes`): [bee routing](../reference/bee-routing.md). Project-local overrides that must not be committed live in `*.local.yaml` (gitignored). ### 2.1 Prompt templates Templates live in **`.paseka/prompts/`** — version-controlled, one colony, shareable across machines. Each bee may reference one or two templates from its `bees/.yaml`: | Field | Artifact | Role | | ----- | -------- | ---- | | `system_template` (optional) | `system.txt` | Standing role context — injected by the adapter, not the first chat turn | | `prompt_template` | `prompt.txt` | User/task turn for AFK runs; optional kickoff for interactive chat | When `system_template` is unset, behavior matches the previous single-template model (full prompt as positional argv only). Full variable list, partials, and override precedence: [prompt templates](prompt-templates.md). Bee YAML schema: [bee config](bee-config.md). **Bee config → templates:** ```yaml # .paseka/bees/builder.yaml role: builder adapter: cursor system_template: builder-system.md # optional — role / standing instructions prompt_template: builder.md # user/task turn worktree: true ``` **Rendering:** Go `text/template` at dispatch time. Runtime builds a **PromptContext** from bus event + colony state, writes `prompt.txt` (and `system.txt` when `system_template` is set) under `.paseka/runs///`, then passes rendered strings to the adapter. Colony-wide fallbacks when a bee omits a field: `defaults.prompt_template` and optional `defaults.system_template` in `colony.yaml`. Do **not** store prompts in `~/.config/paseka/` — they belong to the colony and should ride with the repo. Home config only holds secrets and runtime state. **Bee Language vs technical:** UI/docs may say «Scout Bee»; templates can use bee tone for HITL readability. Bus payloads and JSON partials stay technical (`SIGNAL`, `traceId`, etc.) — see [glossary](../idea/glossary.md). ### Machine-local: `~/.config/paseka//` Per-colony state on this machine. Not committed. ``` ~/.config/paseka// ├── config.yaml # secrets refs, NATS URL, adapter env (overridable via PASEKA_NATS_URL) ├── state.json # runtime: active worktrees, last traceId, hive status ├── telegram.yaml # optional: Telegram Human Gateway (not created by init) ├── telegram-notify-state.json # optional: gate notify dedup (runtime) ├── adapters/ # adapter-specific local overrides │ ├── cursor.yaml # CLI binary path, API key env │ └── pi.yaml # Pi CLI binary path, API key env ``` Telegram bot tokens and allowlists stay machine-local — see [Telegram gateway](telegram-gateway.md). **Split rule:** | Kind | Project `.paseka/` | Home `~/.config/paseka//` | | ---- | ------------------ | ------------------------------- | | Bee roles & adapter choice | yes | — | | Prompt templates (shareable) | yes | — | | API keys, tokens | — | yes (or env var refs) | | NATS connection override | — | yes | | Active worktrees registry | pointer only | authoritative state | | Active agent runs registry | pointer only | optional mirror in `state.json` | | Event replay cache | — | yes | --- ## 3. Project slug Stable identifier for the home config directory. 1. If `origin` remote exists → canonical slug from host/path (e.g. `github.com-acme-api` → `acme-api`, or full `github-com-acme-api`). 2. Else → sanitized directory name of repo root (e.g. `paseka`). 3. Collision on same machine → suffix with short hash of absolute repo path. Stored in `.paseka/colony.yaml` as `slug` after first `paseka init` so later commands resolve the same home path. --- ## 4. `paseka init` Run from inside a git repository (or at repo root). ``` paseka init [--adapter cursor|pi] │ ├─► resolve git root (fail if not a repo) ├─► compute / persist project slug ├─► create .paseka/colony.yaml (defaults) ├─► create .paseka/prompts/ with starter templates (scout, builder, hivewright) ├─► create .paseka/bees/ with starter bees (scout, builder, hivewright) for the selected adapter ├─► create .paseka/cues/ with starter cues (feature, hotfix) when missing ├─► create .paseka/.gitignore (worktrees/, runs/, *.local.yaml, cache/) ├─► create ~/.config/paseka//config.yaml ├─► create ~/.config/paseka//state.json (empty) ├─► create ~/.config/paseka//adapters/.yaml (cursor by default; pi when --adapter pi) └─► print next steps (adapter-specific auth / CLI setup, then `paseka run`) ``` `--adapter` selects which LLM adapter the starter bees use (`cursor` default; `pi` supported). Unknown adapter names fall back to `cursor`. `paseka init` is idempotent: existing files are preserved; missing pieces are added. --- # Source: architecture/overview.md --- # Architecture overview Adapters, run IPC, worktrees / code proposals, end-to-end flow, and Go package layout. Colony config layout and `paseka init`: [Colony layout](../guide/colony-layout.md). ## 1. Agent adapters An **adapter** is a thin driver: prepare workspace → invoke external tool → normalize result → emit bus events. Paseka does **not** implement agents. It launches ready-made solutions with the right cwd, prompt, and parameters. ### Adapter interface (Go) ```go type Adapter interface { Name() string Run(ctx context.Context, req RunRequest) (*RunResult, error) } type RunRequest struct { Bee string Prompt string ColonyRoot string // git root — runs/ always under colony Workspace string // cwd for adapter (repo root or worktree) Params RunParams TraceID string // flight trail for the whole task chain AgentID string // unique id per spawned agent } type RunResult struct { Status string // completed | failed | cancelled Output string // stdout / final assistant text Artifacts []Artifact // diffs, logs, structured JSON ExitCode int } ``` Adapters live under `internal/adapters//`. Registration is declarative via `adapter` field in bee config. ### 1.1 File-based agent IPC (`runs/`) Each spawned agent gets an isolated directory under the **colony root** (not inside a worktree), so results survive worktree cleanup and multiple agents can share one `traceId`. ``` .paseka/runs/// ├── prompt.txt # runtime → agent: rendered prompt_template (audit / replay) ├── system.txt # optional — rendered system_template (adapter injection) ├── summary.md # runtime log: human-readable summary (not a success contract) ├── meta.json # runtime → observers: bee, adapter, workspace, startedAt ├── status.json # runtime → observers: completed|failed, exitCode, finishedAt ├── session.json # interactive only: pid, state, session metadata └── transcript.ndjson # interactive only: dialogue audit log ``` Task ledger projection (updated by `paseka run`): ``` .paseka/runs//tasks// ├── task.md # markdown + YAML frontmatter task snapshot └── runs.ndjson # links task executions to agent run directories ``` | ID | Scope | Generated by | | -- | ----- | ------------ | | `traceId` | Whole flight trail — one bloom/nectar chain | runtime (`colony.NewTraceID`: `trace-` + 16 hex, time-ordered) | | `agentId` | Single adapter invocation (one `agent` process) | runtime (random hex) | **Why colony root, not worktree:** code edits happen in `.paseka/worktrees//`, but agent I/O and audit trail live in `.paseka/runs///`. Prompt uses an **absolute path** to `summary.md` so Cursor CLI writing from a worktree cwd still lands in the colony runs dir. Entire `runs/` tree is **gitignored** — ephemeral, machine-local artifacts. Implementation: `internal/runs/` prepares directories and files; adapters may still read legacy `result.txt` content for summary normalization, but run success no longer depends on it. Runtime writes new runs to `summary.md`. Runtime auto-synthesizes `INSIGHT/run.summary` when policy allows. Domain events are published by agents through `paseka event emit --stdin`, not by parsing assistant stdout. **Event publish path (MVP):** ```text agent -> paseka event emit --stdin -> validation -> NATS/JetStream ``` Agents build one JSON object per event, pass it on stdin, and receive machine-readable validation/publish feedback. `events.ndjson` is the per-run audit log under `.paseka/runs///`; `paseka event emit` appends there after a successful publish when the event includes the correct `traceId` and `agentId`. **Optional MCP layer:** a future MCP tool may wrap the same validation/publish backend used by `paseka event emit`. MCP is not required for the base contract. ### Example: Cursor adapter (CLI) **Decision:** invoke the **Cursor Agent CLI** (`agent`), not the SDK. Prototype: `fizman-parent/scripts/ai-tasks-run.sh` (tmux wrapper → simplified in Go via `exec`). | Input (bee config + event) | Maps to `agent` flag | | ---------------------------- | -------------------- | | `command` (optional) | full argv; overrides `params` mapping (see [bee config](../guide/bee-config.md)) | | `Workspace` | `--workspace ` (repo root or `.paseka/worktrees//`) | | `Prompt` | positional prompt argument (`system_template` + task, newline-separated, when system is set) | | `params.model` | `--model ` | | `params.trust` (default true) | `--trust` | | `params.force` (default true) | `--force` | | `params.output_format` (default `stream-json`) | `--output-format stream-json` | | `params.mode: plan` | `--plan` | | API key | `CURSOR_API_KEY` env or `--api-key` from home config | Default non-interactive invocation (same spirit as fizman script): ```bash agent -p --trust --force \ --workspace "$WORKSPACE" \ --output-format stream-json \ "$PROMPT" ``` **Result collection:** 1. **Process outcome** — adapter reports exit/cancel status; runtime may downgrade via `completion_contract` and per-bee `run_summary` policy. 2. **Run summary** — runtime auto-publishes `INSIGHT/run.summary` when allowed and missing; agents may emit it explicitly via `paseka event emit`. 3. **Log artifact** — runtime writes normalized summary to `summary.md` for human inspection. 4. **Git diff** — after `agent` exits, capture a **baseline-attributed** tracked diff in the **workspace** (worktree or repo root). Pre-existing dirty files are not attributed to the run. 5. **Stream JSON** — stdout when `output_format: stream-json` (lifecycle/diagnostic parse only; domain events are not extracted from assistant text). When the final `result` line includes `usage` (`inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`), the adapter persists it on `result.json` as optional `usage` (source `cursor.stream-json`). Adapters without usage omit the field; Honey Reserve (`energyToken`) stays dispatch-count based and is unrelated. 6. **status.json** — runtime records exit code and outcome for `paseka inspect` / Queen Console. Go implementation: `internal/adapters/cursor/` runs `agent` with `exec.CommandContext` (no tmux — process wait replaces the shell's `tmux wait-for` pattern). Optional: Cursor's built-in `--worktree` flag exists but Paseka prefers **`.paseka/worktrees//`** under colony control for HITL merge/reject. ### Example: Pi adapter (CLI) **Decision:** invoke the **Pi CLI** (`pi`) for bees configured with `adapter: pi`. AFK runs use `pi -p`; interactive sessions use `pi` under a Paseka-owned PTY (see [interactive sessions](../guide/interactive-sessions.md)). | Input (bee config + event) | Maps to `pi` flag | | ---------------------------- | ----------------- | | `command` (optional) | full argv; overrides `params` mapping (see [bee config](../guide/bee-config.md)) | | `Workspace` | process cwd | | `Prompt` | positional prompt argument | | `params.model` | `--model ` | | `params.provider` | `--provider ` | | `params.thinking` | `--thinking ` | | `params.output_format` | `--mode ` (AFK only; see below) | | `params.plan` | `--plan` | | `params.binary` | CLI binary name (default `pi`) | | API key | `api_key_env` from `~/.config/paseka//adapters/pi.yaml` → `--api-key` | **`output_format` → `--mode` (AFK only):** | `params.output_format` | Pi `--mode` | | ---------------------- | ----------- | | `text` | `text` | | `json` | `json` | | `rpc` | `rpc` | | empty or any other value | `json` (default) | Default non-interactive invocation: ```bash pi -p --mode json \ --model "$MODEL" \ --provider "$PROVIDER" \ "$PROMPT" ``` **Ignored params:** Pi does not map Paseka `trust` or `force` (no equivalent flags). **Result collection:** 1. **Process outcome** — adapter reports exit/cancel status; runtime may downgrade via `completion_contract` and per-bee `run_summary` policy. 2. **Run summary** — runtime auto-publishes `INSIGHT/run.summary` when allowed and missing; agents may emit it explicitly via `paseka event emit`. 3. **Log artifact** — runtime writes normalized summary to `summary.md` for human inspection. 4. **Git diff** — after `pi` exits, capture a **baseline-attributed** tracked diff in the **workspace** (worktree or repo root). 5. **Stdout** — raw stdout is preserved as an artifact. In `json`/`rpc` modes the adapter tolerantly extracts a human summary from common JSON fields (`summary`, `output`, `text`, etc.) for `summary.md` only. 6. **status.json** — runtime records exit code and outcome for `paseka inspect` / Queen Console. **Event publishing boundary:** Pi stdout/JSON is **not** parsed into domain bus events (`SIGNAL`, `INSIGHT`, `MUTATION`, `VERIFICATION`). Agents must publish domain events explicitly via `paseka event emit --stdin` — same contract as Cursor. **Machine-local config** (`~/.config/paseka//adapters/pi.yaml`): ```yaml binary: pi api_key_env: GEMINI_API_KEY # optional; passed as --api-key when set in env ``` If the file is missing, defaults are `binary: pi` and no API key injection. Example bee config: ```yaml # .paseka/bees/scout.yaml role: scout adapter: pi params: model: gemini-2.5-pro provider: google thinking: high output_format: json prompt_template: scout.md ``` Go implementation: `internal/adapters/pi/`. ### Script adapter (bash / python / custom) **Decision:** bees with `adapter: script` run a **declared command** (bash, python, Go binary, etc.) instead of an LLM CLI. Use for deterministic eval bees (oracle guard, fault-injecting builder), CI hooks, and other signal-driven automation. Script bees are **AFK-only** (`paseka bee run`); `bee chat` remains LLM-only. ```yaml # .paseka/bees/oracle-guard.yaml role: oracle-guard adapter: script command: ./scripts/oracle-guard.sh run_summary: disabled subscribes: - type: MUTATION kind: code.proposal.isolated dispatch: direct publishes: - type: VERIFICATION kind: verification.success - type: VERIFICATION kind: verification.failed ``` **Requirements:** - `command:` is **required** (shell-like string or YAML argv list). - `prompt_template` is **optional** — when omitted, no colony default is applied; when set, the rendered prompt is written to `prompt.txt` and available as `$PROMPT`. - `params` are ignored (runtime logs a warning if both `command` and `params` are set). **Process environment** (in addition to `command` variable substitution): | Variable | Value | | -------- | ----- | | `PASEKA_TRACE_ID` | current `traceId` | | `PASEKA_AGENT_ID` | this invocation id | | `PASEKA_TASK_ID` | task id when dispatched from ledger | | `PASEKA_WORKSPACE` | adapter cwd (repo root or worktree) | | `PASEKA_COLONY_ROOT` | git repo root | | `PASEKA_RUN_DIR` | `.paseka/runs///` | | `PASEKA_BEE` | bee role name | | `PASEKA_EVENT_LOG` | path to `events.ndjson` | | `PASEKA_RESULT_FILE` | path to `summary.md` | | `PASEKA_PROMPT_FILE` | path to `prompt.txt` | **Emitting events:** scripts publish domain events the same way LLM agents do — pipe JSON to `paseka event emit --stdin`: ```bash paseka event emit --stdin <` | `Adapter.Run()` — Cursor: `agent -p`; Pi: `pi -p` | | Interactive | `paseka bee chat ` | `SessionAdapter.SessionCommand()` — Cursor: `agent` without `-p`; Pi: `pi` without `-p`/`--mode`, PTY-owned by runtime | Interactive runs add `session.json` and `transcript.ndjson` under the same `.paseka/runs///` tree. Active sessions are registered in `~/.config/paseka//state.json`. Terminal UI (default terminal vs Ghostty) is configured in `~/.config/paseka//terminal.yaml`. --- ## 2. Worktrees and code proposals Code changes flow through two **proposal paths** distinguished by workspace provenance. Invariant: **a guard bee always reviews the same workspace that produced the diff.** See [specs/008-code-proposal-workspaces.md](../specs/008-code-proposal-workspaces.md). | Path | Publisher | `MUTATION` kind | Reviewer cwd | Human approve | | ---- | --------- | --------------- | ------------ | ------------- | | **Isolated** | `builder` (`worktree: true`) | `code.proposal.isolated` | `.paseka/worktrees//` (+ sector) | Merge trace worktree when present (`review: final` / `_review`); AFK commit-gate defer | | **Root** | `hivewright` (`worktree: false`) | `code.proposal.root` | Colony root (+ sector) | **R1** soft ack only — no worktree merge, no auto-commit | Bare `code.proposal` in bee YAML is an **alias of `code.proposal.isolated`**; runtime normalizes it on auto-publish write. Subscribers of `code.proposal` or `code.proposal.isolated` match isolated events; `code.proposal.root` subscribers do not. ### Isolated path (trace worktree) ``` SIGNAL / INSIGHT on bus │ ▼ Bee assigned (e.g. builder + worktree: true) │ ▼ WorktreeManager.Create(traceId, baseBranch) │ → .paseka/worktrees// (gitignored) ▼ Adapter.Run(Workspace = worktree path) │ ▼ Baseline-attributed git diff in worktree │ ▼ Publish MUTATION/code.proposal.isolated (+ provenance) │ ▼ guard (same worktree, direct dispatch) reviews disk │ ▼ Human review → approve (merge when final gate) | reject ``` ### Root path (colony checkout) ``` task.ready → hivewright (worktree: false, cwd = colony root) │ ▼ Baseline-attributed git diff on colony root │ ▼ Publish MUTATION/code.proposal.root (+ provenance) │ ▼ main-guard (colony root, direct dispatch) reviews disk │ ▼ review: required → waiting_review (R1 ack; no merge) ``` Root proposals do **not** open the AFK receiver commit-gate defer (`waiting_review` for merge only applies to isolated proposals). Beekeeper commits root changes manually. ### Shared details **Default worktree location:** `.paseka/worktrees//` — colocated with colony, simple paths for adapters, listed in `.gitignore`. **Branch:** `paseka/` (registered in machine-local `state.json`). **Direct dispatch workspace affinity:** isolated (+ alias) → ensure/reuse trace worktree (prefer existing dirty tree over fresh HEAD); root → colony root, never call worktree ensure. **Auto-publish:** runtime publishes a proposal only when the bee explicitly declares the matching kind in `publishes` and `worktree` matches the kind. Empty `publishes` never auto-publishes (fail closed). Mismatch → skip + warn; hard mismatches are `paseka doctor` errors (see [bee config](../guide/bee-config.md)). **Merge preview:** before approving an **isolated** final merge gate (`review: final` / `_review`), Queen Console loads a three-dot diff of `defaultBranch...paseka/` via `worktree.MergeDiff` and `GET /api/traces/:traceId/merge-diff` (unified patch + `--stat`, truncated at 1 MiB). See [specs/002-queen-console-mvp.md](../specs/002-queen-console-mvp.md). **Registry:** `~/.config/paseka//state.json` tracks active worktrees, base SHA, branch, and linked `traceId` for cleanup on `paseka doctor`. Commands (later): `paseka worktree list`, `paseka worktree clean`. --- ## 3. End-to-end flow A single `traceId` may contain multiple tasks (`taskId`) managed by the Task Ledger. See [task ledger](../reference/task-ledger.md) for the `task.plan → task.ready → task.completed` protocol. ```mermaid flowchart LR subgraph repo [Colony — git repo] PC[.paseka/colony.yaml] PB[.paseka/bees/*.yaml] WT[.paseka/worktrees/] end subgraph home [Apiary — home config] HC[~/.config/paseka/slug/] ST[state.json] end subgraph runtime [Hive Runtime — Go] QS[Queen Shell CLI] WM[WorktreeManager] AD[Adapters] BUS[NATS / JetStream] end subgraph external [External agents] CR[Cursor Agent CLI] PI[Pi CLI] end QS --> PC QS --> HC BUS --> AD AD --> WM WM --> WT AD --> CR AD --> PI CR --> WT PI --> WT AD --> BUS ``` --- ## 4. Package layout (target) ``` cmd/paseka/ # Queen Shell internal/ colony/ # load .paseka + home config, slug resolution prompts/ # load + render .paseka/prompts/*.md templates runs/ # .paseka/runs/// layout + meta/status adapters/ # adapter registry + cursor/, pi/, … sessions/ # interactive PTY sessions, terminal attach worktree/ # create, diff, merge, cleanup bus/ # NATS, message contracts runtime/ # dispatch: colony → prompts → adapter (AFK) ``` --- ## 5. Decisions (locked) | Topic | Decision | | ----- | -------- | | Worktree path | `.paseka/worktrees//` — colony-managed; registry in home `state.json` | | Cursor invocation | Cursor Agent CLI (`agent`) — port of `ai-tasks-run.sh` pattern | | Pi invocation | Pi CLI (`pi`) — AFK `pi -p`, interactive PTY; see §1 Pi adapter | | Supported adapters | `cursor` (default), `pi` — selected per bee via `adapter:` in `bees/*.yaml` | | Agent run IPC | `.paseka/runs///` — file-based; entire `runs/` gitignored | | Prompt templates | `.paseka/prompts/` — committed; bee YAML references `prompt_template` and optional `system_template` | | Commit `.paseka/` | yes by default; `.gitignore` covers `worktrees/`, `runs/`, `*.local.yaml`, `cache/` | | Slug in colony.yaml | written at `paseka init`, reused on every run | | Interactive sessions | separate `SessionAdapter`; PTY in `internal/sessions/`; see [interactive sessions](../guide/interactive-sessions.md) | | Terminal UI for HITL | `~/.config/paseka//terminal.yaml` — `default` or `ghostty` | ### `.paseka/.gitignore` (created by `paseka init`) ``` worktrees/ runs/ *.local.yaml cache/ ```