Architecture

On this page

Sam runs from code up to behaviour. The layers answer “how is Sam loaded”; recipes answer “what does Sam do”.

Architecture

Layers

layer what lives there
runtime daemon, cron, webhooks, secrets, storage, and every connector (GitHub, Notion, Slack, Linear). Deterministic code. Tier 3.
identity who Sam is
scope boundaries: principal, forbidden actions, working hours
skills substrate skills in src/skills/, recipe-owned skills in src/recipes/*/skills/
behaviour what the team experiences: delivery, self-improvement, workspace ops

Everything below identity is enforced by code. Everything above it is prompt. src/capabilities/self-maintenance.md names the rule: prose explains, the runtime enforces.

Recipes

A behaviour is a vertical slice through those layers, not a file in one of them. A recipe packages the slice: a recipe.md manifest (connectors, triggers, communication contract, scope deltas) plus the skills it owns.

flowchart TB
  sd["software-delivery"] --> rt["runtime primitives"]
  si["self-improvement"] --> rt
  wo["workspace-ops"] --> rt
recipe communication contract declares
software-delivery thread-scoped, batched, quiet hours respected github, linear, slack
self-improvement daily broadcast to #sam, never urgent github, slack
workspace-ops confirmation-first, no PR/CI vocabulary notion, slack

The contract is the point. Different behaviours use Slack differently, and one global policy can’t serve them all. Where a recipe is silent, the defaults in src/scope.md hold.

Two rules keep the boundary clean:

Activation

The daemon is recipe-blind today. Every session gets all manifests; Sam matches the work against them in context, the same way skills are selected.

sequenceDiagram
  participant Event
  participant Daemon
  participant Session
  Event->>Daemon: mention, cron fire, or webhook
  Daemon->>Session: prompt carrying every recipe manifest
  Session->>Session: pick the recipe, apply its contract
trigger how the recipe is determined
cron routed in practice: the scheduled message names a skill path inside a recipe
webhook routed in practice: the event playbook lives in software-delivery
mention model judgment

Planned: the daemon matches declared triggers and stamps the active recipe into the session preamble and the ledger. That makes the first two rows deterministic. The third stays model judgment; classifying free-form language is the model’s job.

Composition

Recipes don’t call each other. They share artifacts: the journal, Linear, Slack threads, PRs. One recipe writes, another reacts.

flowchart LR
  sd["software-delivery"] <--> a[("shared artifacts")]
  si["self-improvement"] <--> a
  wo["workspace-ops"] <--> a

Attribution

Related