Tech Lead · Software Architect · Agentic AI EngineerOpen to new work · İzmir, Turkey · UTC+3

The AgentEff architecture

AgentEff is a software factory of Effect 4 actors, driven from Slack, in which the models propose and deterministic code decides. It is in design: planning finished in October 2026, and the build is starting.

AgentEff is a software factory Emre is designing. People direct it from Slack, and it is meant to take work from a request to tested software running in environments it manages itself.

This post describes a design, not a system. Planning finished in October 2026 and the build is starting; nothing on this page has run in production, and there are no results to report. Where a number appears, it is either a design target, and says so, or sample data in the illustrative scenario at the end.

The design hangs from one principle: deterministic workflows, probabilistic intelligence. The agents in AgentEff — one Director, Squad Leads under it, Workers under them — are model-driven, and a model may propose a plan, a diff or a risk score. Whether that proposal is acted on, charged, retried, approved or deployed is decided by deterministic code that can be read, tested and replayed. No guarantee in the design depends on a model behaving.

The drawings below come from the architecture spine, which records forty-two numbered decisions, AD-1 to AD-42, each with what it binds and what it prevents. The AD numbers beside each drawing say which decisions it shows.

Skip to the live scenario ↓

  • Deterministic
  • Probabilistic
  • Human gate
  • Enforcement
  • External tool
  • Storage
  • — the colours in every drawing below

Layers, and the rule between them

  • AD-1
  • AD-2

The paradigm is three ideas used together: event-sourced actors, a functional core and ports and adapters. Every stateful unit is an Entity on Effect Cluster whose state is a fold of its own event stream. Workflows orchestrate, and hold no business fact that is not also on a stream. Business decisions are pure functions. Every I/O slot is a port, a Context.Service, filled by an adapter, a Layer.

Layers are only worth drawing if the arrows between them are enforced. Each unit is its own Bun workspace package, and a CI import-graph check over package dependencies rejects any edge that is not in the drawing: domain imports nothing, core never imports an adapter, and no adapter imports another. Every port ships a contract test suite that each of its adapters runs, which is what makes a slot swappable rather than nominally pluggable. It is the functional core on Systems, with the boundary checked by the build instead of by review.

Scroll sideways to see the whole drawing.

apps/dead-man imports domain only deployed outside Hetzner apps/* composition roots · may import any unit runner ingress model-gateway egress-proxy core Entities · Workflows · Procedure engine outbox · projectors · reactors reconcilers · RootSupervisor adapters/* one Layer per slot option PgEventStore · IncusSandbox · … never core, never another adapter harness-ext pi extension that runs in the Sandbox ports one Context.Service per slot, each with a contract test suite infra/ Alchemy v2 stacks, run via the Provisioner port domain (pure) no I/O · imports no other unit Schemas · events · commands · errors · decide / evolve · ladder · rail · placement · precedence · lifecycle · route · isFrozen · authorize Provisioner adapter only deploys imports
Arrows are the only allowed imports. A CI import-graph check over the workspace packages rejects any other edge (AD-2), and every port’s contract suite runs against each of its adapters.
Each port and its v1 adapters
SlotPortv1 adapters
Event StoreEventStorePostgreSQL
Model providersLanguageModel (gateway only)OpenRouter; an own Bedrock Layer; OpenCode Go, optional
Decision ModelDecisionModelTypeSafe, Cloudflare
Harness · SandboxHarness · Sandboxpi with harness-ext, Claude Code · Incus
Compute · deploy targetsProvisioner · DeployTargetAlchemy v2 on Hetzner, Incus · Tenant Environments
IntegrationsTracker · Docs · Forge · ChatLinear · Notion · GitHub and Gitea · Slack
Secrets · notification · telemetrySecretLeases · Notifier · Telemetryself-hosted Infisical · Slack, Pushover · OpenTelemetry to LGTM
Memory and KnowledgeMemoryStore + KnowledgeStorePostgreSQL, behind one facade

One way in, one way out

  • AD-4
  • AD-5
  • AD-17
  • AD-31
  • AD-35
  • AD-37
  • AD-42

Humans speak through Slack and tools through webhooks, and everything enters at apps/ingress. Ingress verifies the envelope, Schema-decodes the delivery into an …Observed event, derives a commandId from the delivery ID and routes it to exactly one owning Entity. Everything leaves through the outbox: an intent event, one adapter call, then a result event back to the owner.

A Slack message arrives as one of three types, and only two of them can change anything. A Command needs an Operator or an Engineer acting within their role. A Client Owner’s signed answer to a question routed to them is an Approval, never a Command. Everything else — including an instruction found in a repository file or in a model’s output — is Content, and causes no action.

The Sandboxes are where the models work, so they see the least. A Sandbox can reach the Model Gateway and its egress proxy and nothing else; it holds a short-lived gateway lease and no Slack, Linear or Notion credential at all, and forge tokens are injected by the proxy without the Sandbox ever seeing them. Its one write of its own is a git push to its Run’s branches. Every model call in the Factory, from any Agent, goes through the one gateway, which is also the meter.

The alarm for a dead Factory lives outside it. RootSupervisor sends a heartbeat to a dead-man’s switch that shares no infrastructure with the Factory, and the switch alerts the Operator when the heartbeat stops.

Scroll sideways to see the whole drawing.

AgentEff Factory · Hetzner private network People Operator Engineer Client Owner Slack AD-35 Command Operator · Engineer only Approval signed, within role scope Content never an instruction Work tools Linear Notion GitHub Gitea events hooks apps/ingress verify envelope bad signature → drop Schema-decode → …Observed event commandId uuidv5(tool:delivery) sourceVersion older → ObservedStale route() one addressee Agent Command · Approval via Roles, else Content Persisted apps/runner × N Effect Cluster · one Cluster per Factory RootSupervisor Singleton watch loop · Audit Watcher · FreezeView · no model port Agent Entities · one per Agent Director Squad Leads Workers Entities · single writer per stream Run Ticket Approval Budget Capacity Audit Config Knowledge Topology Identity DocPage core · Workflows orchestrate Workflows Procedure engine projectors · reactors by position outbox intent → call → result event reconcilers Provisioner · DeployTarget Pool: secrets Infisical self-hosted · data keys own PostgreSQL + Redis Sandboxes cannot reach PostgreSQL 18 AD-7 · AD-8 Event Store RLS · tenant_id projections read models infra schema cluster, journal append · read by position pi RPC RPC · ReserveSpend Worker Machine · Incus host default-deny egress ACL apps/egress-proxy per-Sandbox identity typed allowlist injects forge tokens records → Audit Entity isFrozen · fail closed Incus Sandbox · one per Worker Node 24 pi harness-ext gateway lease only no Slack, Linear or Notion credential git push to its Run's branches only plain HTTP static secrets apps/model-gateway per-principal lease ReserveSpend first tier · provider allowlist journal, encrypted isFrozen · fail closed all model traffic journal Outside Hetzner apps/dead-man CF Worker cron Pushover emergency alerts when heartbeats stop heartbeat Outbox writes Slack Linear Notion GitHub Gitea Chat · Tracker · Docs · Forge writes Notifier fallback Reconciler targets Hetzner · Incus APIs Tenant Environments client app Staging · Production TenantSettings: leased reads converge Model providers OpenRouter Phase 1 Bedrock own Layer · Phase 2 OpenCode Go optional DecisionModel TypeSafe/Jev · Cloudflare/Clef forwards OTel → LGTM spans from every service; never the meter spans GitHub · Gitea git push to Run branches only registries · docs allowlisted reads token injected
One way in, one way out. Ingress decodes each delivery into an Observed event (AD-4); the outbox makes every external write (AD-5); the gateway is the only model path and the meter (AD-31); the egress proxy injects forge tokens a Sandbox never sees (AD-17).

A hierarchy, not a swarm

  • AD-11
  • AD-21
  • AD-26
  • AD-30
  • AD-33

The Director is the root Agent and the Operator’s counterpart. It delegates to Squad Leads — PM, Dev, QA, DevOps and SRE — and they delegate to Workers. Each Agent is exactly one Entity with its own mailbox and stream, and a Command reaches an Agent only from its parent. The Director and the Squad Leads never implement; only Workers run Sandboxes and write to repositories.

Above the Agents sits RootSupervisor, which has no model port at all. It judges sickness only from signals no Agent controls — the gateway journal, Run progress, runner heartbeats — and treats it from a fixed list: reply, retry, replay, interrupt, re-instantiate, die. A restart rebuilds the Agent from the Event Store; past its restart limit, an Agent stops restarting and the Operator is told.

A freeze is enforced where the frozen Agent cannot reach. The Audit Watcher is a deterministic rule set with no model in it, and a rule hit or an Operator Command can freeze an Agent, a subtree or a whole Tenant. RootSupervisor publishes the freeze as a versioned view; four enforcement points outside every Agent — the gateway, the egress proxy, the outbox and the admission check — evaluate the same pure isFrozen on every action, and refuse when their view is stale. Only an Operator can lift it.

Scroll sideways to see the whole drawing.

Audit Watcher deterministic, LLM-free rule set reads plaintext envelopes of: · Event Store · gateway journal · Audit Entity per Tenant (egress records, forge pushes) RootSupervisor AD-26 · AD-30 Entity + Cluster Singleton watch loop on control · no model port signals outside Agents: gateway journal, Run progress, heartbeats symptoms: hung · no progress · looping · unresponsive · over limit treatments: reply · retry · replay · interrupt · re-instantiate · die restart circuit breaker · freeze, safe-hold · Factory heartbeat rule hits Freeze & safe-hold sources: Audit Watcher, Operator safe-hold: every Agent of a Tenant lift: an Operator-role Command only always pass: RootSupervisor, Operator Commands, compensations owns Director one Entity · fixed Internal ID reads Portfolio Projections only restarts it, no Human needed Lifecycle · pure transition in domain/ Born Grow Sleep Grow, Sleep → Reproduce → Grow any state → Die (journaled Workflow) Commands only from the parent PM Squad Lead Worker Sandbox Worker Sandbox Dev Squad Lead Worker Sandbox Worker Sandbox QA Squad Lead Worker Sandbox Worker Sandbox DevOps Squad Lead Worker Sandbox Worker Sandbox SRE Squad Lead Worker Sandbox Worker Sandbox only Workers write to repositories; the Director and Squad Leads never implement watches and treats every Agent FreezeView: versioned, monotonic epoch → isFrozen(view, agentTree, originatingActor), fail closed when older than freeze.maxStaleness apps/model-gateway refuses the model call apps/egress-proxy revokes the subtree's leases outbox refuses the intent admission check starts no attempt publishes FreezeView
Freeze is enforced where the Agent cannot reach. The gateway, the egress proxy, the outbox and the admission check each evaluate the same pure isFrozen against a versioned FreezeView, and fail closed when it is stale (AD-33).

The write path

  • AD-3
  • AD-4
  • AD-5
  • AD-7
  • AD-8

Every state change has one shape. A command, carrying a commandId derived as a uuidv5 of its cause, reaches the single Entity that owns the stream. A pure decide(state, command) returns events — no I/O, no clock, no model — and they are appended at the expected version. A command whose ID is already in the stream returns the earlier result and appends nothing, so a redelivered webhook or a restarted reactor is harmless.

Readers see an event only once every lower position has committed, and projectors and reactors consume by position. Side effects run only as outbox intents with result events — the outbox on Systems, taken all the way. A crash between a call and its result yields …InDoubt, and an irreversible step in doubt is never retried automatically; it goes to the role that owns it. Every Tenant-scoped table carries a row-level security policy on tenant_id, and CI rejects a migration that adds one without it — tenancy enforced by the database, not by the caller.

Scroll sideways to see the whole drawing.

1 Work tools Slack Linear Notion GitHub Gitea each delivery carries a delivery ID and a sourceVersion echoes carry causedByIntent 2 apps/ingress decode → …Observed commandId = uuidv5 (tool:delivery ID) Persisted message 3 owning Entity single writer of its own stream; mailbox keyed by its Internal ID 4 decide decide(state, cmd) → events pure: no I/O, no clock, no model 5 append at expected version (optimistic lock); a seen commandId returns the earlier result, appends nothing 6 Event Store global position tenant_id under RLS actor stamped by the runtime 7 projectors and reactors consume by position, at least once, with a per- consumer checkpoint 8 outbox intent …Requested event starts an Outbox Workflow; exec ID = intent eventId 9 adapter call Tracker · Docs · Forge · Chat · Notifier idempotency key from intentId 10 result event Succeeded · Failed · PreconditionFailed · InDoubt: no auto retry if irreversible reactors send new commands to an owner, with commandId from (sourceEventId, reactor name) delivery command state events commit by position intent call outcome RecordIntentResult one external write, embeds intentId; its echo returns as a delivery
Idempotent at every hop. A commandId is a uuidv5 of its cause, so a repeated delivery appends nothing (AD-3); readers follow commit order (AD-7); every external write is an intent with a result (AD-5).

A Run, end to end

  • AD-6
  • AD-16
  • AD-31
  • AD-40
  • AD-41

A Run is one execution of a Procedure, and one cycle of a Ticket is one Run. Its facts — attempts, the model tier, the pinned version of everything it uses, its status — live on a Run Entity’s stream, not in the Workflow’s journal; the Workflow only orchestrates. Each attempt begins with StartAttempt, which the Run Entity refuses once the pinned retry budget is spent, and no command a Worker can reach resets it.

Model work never runs in an Entity’s handler, which only decides, appends and starts Workflows. It runs as a Workflow Activity: the Worker drives the pi harness over RPC inside its Sandbox, and each model call goes to the gateway, which resolves the Agent, Run, Budget node and tier from the lease, asks the leaf Budget Entity to reserve the spend, forwards only on a grant, then settles and journals the pair.

Because every pair is journaled, a replay needs no model calls: the Workflow re-reads its Activity results, a replay provider serves the journal, and the outbox swaps its adapters for recorders that do nothing. A refusal from the gateway is a closed, typed set, and each kind maps to one fixed outcome for the attempt.

Scroll sideways to see the whole drawing.

Linear via apps/ingress Ticket Entity Artifact Entity Workflow (ticketId, cycle) Run Entity agenteff:run/<uuid> Activity Sandbox · pi RPC Model Gateway apps/model-gateway Budget Entity leaf Budget node Provider OpenRouter · Bedrock …Observed start cycle Definition of Ready StartAttempt pins versions; refuses past the retry budget AcquireSlot · isFrozen run agent step as an Activity prompt · steer · abort model call + lease lease → agent, run, tier ReserveSpend grant forward response journal pair, encrypted SettleSpend response Activity result, journaled RunFinished Replay the Workflow re-reads journaled Activity results · a pi replay provider serves the gateway journal outbox adapters become no-op recorders · egress write routes close · result: zero model calls (FR-26) no calls
The facts live on streams, not in the journal. Only the Ticket’s Entity starts the (ticketId, cycle) Workflow, and only a Human raises the cycle (AD-6). The Sandbox holds a gateway lease and no provider key (AD-16, AD-31).
  • BudgetExhausted · Frozensuspend the attempt; no retry budget spent
  • BreakerTripped · ProviderRateLimitedwait on a durable timer
  • TierRefused · ProviderNotAllowedfail the attempt, non-retryable

The decision ladder

  • AD-13
  • AD-20
  • AD-35
  • AD-36

Who must approve an action is decided by a pure function, a ladder of six rungs. Its inputs are the step’s declaration, the Ticket’s risk, the paths in the diff and a Decision Model’s risk score compared with the Mode’s threshold — so a model’s output is one input among several, never the verdict. Rung 1 rejects on first match. Rungs 2 to 5 each add an approver, and all of them must approve. If no rung matches, the action is reversible and runs on its own, and the decision is logged with its rungs, inputs and confidence.

An Approval binds to the exact bytes it approves. Every request carries a subjectDigest, the hash of the inputs the step will act on, and the step recomputes it when it runs: any change voids the grant. A grant that covers several items is refused for an irreversible step or a Production deploy.

Production has a single door. A Production release changes only through SetProductionRelease, which is accepted only with a Human Approval whose digest is that of the Production-Readiness Certificate, and the certificate is built and signed by a model-free step outside every Sandbox. During an incident, a declared containment step may switch traffic back to the last approved release, or turn off a kill-switch flag, without waiting for anyone — but it never changes the release of record, and an overlay nobody confirms escalates instead of quietly flipping back.

Scroll sideways to see the whole drawing.

action from a declared Procedure step inputs: step declaration · Ticket risk · DecisionModel score vs Mode threshold · diff paths 1 Forbidden by an Agent Rule or policy, including a Capability Profile 2 Irreversible declared per step: no declared compensation 3 Touches a Domain Rule changes it, or an Artifact anchored to its Internal ID 4 High-risk score over threshold · auth, secrets, payments, infra · new dependency 5 DecisionModel unsure an unsure outcome is never approved autonomously 6 No rung matched the action is reversible and nothing above applies Rejected · no later rung runs only the Operator grants an exception, recorded match Approvers accumulate · all must approve a Human of the owning role, always adds that Tenant's Client Owner adds an Engineer or the Operator adds stronger-model review, then a Human adds Autonomous, logged every decision records its rungs, inputs, confidence
Rejection is first-match; Approvals accumulate. The same inputs always give the same rejection or the same set of approvers. The yellow rows are Humans (AD-13).

Scroll sideways to see the whole drawing.

Digest binding · AD-13 Approval request subjectDigest = hash of the exact inputs the step acts on grant: an Approval never a Command; one item only for an irreversible step step executes recomputes the digest over its current inputs digest changed? the grant is void; a new request goes out Single Production path · AD-20 · AD-36 the only command that changes a Production release PRC built by a model-free step outside every Sandbox, content-addressed, signed Approval subjectDigest = prcDigest a Human with the production role, one item SetProductionRelease {environmentId, buildHash, prcDigest, approvalId} → Topology Entity decide reconciler apply re-verifies PRC signature and hashes in the deploying step; drift repair too
A grant is bound to its subject. The step re-checks the digest when it executes, and the Deployment Procedure’s last step only issues SetProductionRelease; it never deploys itself (AD-13, AD-20, AD-36).

Scroll sideways to see the whole drawing.

Containment overlay over a Production Environment · AD-20 release of record: only a granted SetProductionRelease changes it, and one that applies lifts any overlay containment overlay releaseSwitch{buildHash, prcDigest} | flagOff{service, flag} unconfirmed: stays in force, held until an Operator-role Command lifts it ApplyContainmentOverlay declared step · no prior Approval containment.confirmWithin production-role Human confirms, else escalate toward safe-hold containment.overlayTtl Config values, durable timers confirmed → lifts refused when the current release shipped irreversible data migrations, or no approved release is retained
Containment without a deploy. An overlay is laid over the release of record and never changes it; the two durations are Config values, not fixed numbers (AD-20).

Memory and Knowledge

  • AD-14
  • AD-15
  • AD-32

The Event Store is the only truth, and what the Factory learns is split in two. Memory is what an Agent writes freely about its own work, anchored to a Ticket, a Project or a Squad, and it needs no Approval. Knowledge — Domain Rules, decision records, stack and Factory conventions — changes only by one of three paths: an Approval, a Project-level decision record ratified by the owning Squad Lead and revocable by a Human, or a setting edit observed from the tool. Domain Rules and stack- or Factory-wide Knowledge always need a Human’s Approval. Promotion is the only path from Memory to Knowledge: the consolidation pass the design calls Dreaming may write Memory and may only propose Knowledge.

Every write is scanned for secrets before it is decided, and holds a pointer to a variable’s source, never its value. Staleness is decided by a deterministic projector — an item turns suspect when its anchor changes and expires on a timer — never by a model, and Agents read through one facade that serves a stale item labelled rather than trusted. Retrieval in v1 follows typed links in Postgres, with no knowledge-graph engine.

Personal and Tenant-confidential fields are encrypted with per-Tenant or per-subject keys at every serialization boundary from the first phase, and the keys live outside the Factory’s database and its backups. That is crypto-shredding readiness: erasing a subject means destroying a key, in an append-only log that otherwise never forgets.

Scroll sideways to see the whole drawing.

Knowledge write paths an Approval (always Human for Domain Rules, stack, Factory scope) agent-ratified Decision Record Observed direct setting edit Change Signals reactor deterministic, over Observed → candidate Domain Rule Changes Dreaming on the Squad Lead's Entity writes Memory · proposes Knowledge producing Agent Entity appends Memory, no Approval; anchored to Ticket, Project, Squad SecretScan · in the handler, before decide Event Store the truth Knowledge Entity one per scope: Factory, stack, Tenant, Project Decision Records supersede; a Domain Rule keeps its ID “rule on date D” = as-of query Memory events on Agent Entities; Squad scope on the Squad Lead pointers to Variables, never their values Promotion via Approval Knowledge projections trust labels · as-of reads OKF bundle in git exported projection for Humans; not an Agent read path staleness projector suspect on an anchor's change event · expired on a timer event Memory read models keyed by anchor, never by Agent MemoryStore + KnowledgeStore one facade over two slots RLS · Tenant-authorized items suspect or expired served labeled Context Pack compiler per Ticket, compiled ahead, cached; recompiles on a Ticket, design, plan, commit, Knowledge or template change Agent (Worker) reads its Context Pack; searches beyond it are logged misses its work becomes new Memory events on its own Entity
Promotion is the only path from Memory to Knowledge. Dreaming writes Memory and may only propose; staleness is decided by a projector, never a model (AD-14, AD-15).

What it costs A lost key is lost data by design. The fields it encrypted cannot be read again, and the design accepts that as the price of an erasure that works in an append-only log.

The shape on Hetzner

  • AD-18
  • AD-23
  • AD-24
  • AD-25
  • AD-42

The Factory has three environments: local, which is a Nix shell and one runner; staging; and production. Staging is isolated — its own database, Slack app, Linear team, keys and Budgets — and never receives production webhooks or credentials, and a change to the Factory itself reaches production only with the Operator’s Approval. Hosts are reproducible NixOS machines, installed from pinned images, and provisioning is Alchemy v2 stacks run through the Provisioner port.

Production is four kinds of Machine Pool on one private network whose default is deny. control runs PostgreSQL, ingress, the gateway and the runner for the Director, RootSupervisor and the Squad Leads. shared-workers runs Worker runners and their Incus Sandboxes, each behind the host’s network ACL and an egress proxy. secrets runs a self-hosted Infisical with its own database, on a failure domain apart from control, and hosts no Agent. Each Tenant gets a tenant:<id> pool that hosts its own client app’s Staging and Production and no other Tenant’s.

A backup counts only after a restore drill has restored it. The design targets a recovery point of 15 minutes or less and a recovery time of 4 hours or less, with a monthly drill that also restores the secrets plane and proves the encrypted fields still decrypt; the sizing target is 20 concurrent Sandboxes and their runners. Those are design targets. None of them has been measured, because nothing has been deployed.

Scroll sideways to see the whole drawing.

Factory local Nix shell + one apps/runner Factory staging · isolated own Postgres, ingress, gateway, runners, secrets Machine and Infisical own Slack app, Linear team, keys, Budgets · no production webhooks Factory production drawn below; a self-change arrives only with an Operator Approval change Operator Approval infra/ · Alchemy v2 stacks via the Provisioner port; one state store + run lock per env nixos-anywhere --kexec pinned nixos-images 26.05 tarball; kexec test per type NixOS 26.05 hosts kernel in ZFS's supported range, never linux_latest; hostId set Nix binary cache harmonia or niks3; closures for every host closures hosts Hetzner Cloud · private network · default-deny Pool: shared-workers Incus host · Cluster runner Sandbox ACL apps/runner Worker Entities Incus Sandboxes on ZFS one system container per Worker egress: gateway + proxy only apps/egress-proxy sidecar HTTP more Incus hosts as the Topology sizes the pool: each with one runner and one egress proxy Pool: tenant:<id> client app Staging client app Production Tenant runners, Sandboxes when the Topology places them hosts no other Tenant's Agents or Environments one Incus project per Tenant Pool: control apps/model-gateway only model path apps/ingress always on PostgreSQL 18.6 on ZFS · WAL to PITR apps/runner Director, Squad Leads, RootSupervisor Pool: secrets one server · no HA in v1 Infisical, self-hosted secrets · data keys · no Agents on this pool PostgreSQL + Redis its own root secrets sops-nix/agenix · age key keys model calls denied: Postgres, secrets static secrets git push to Run branches · allowlisted reads Outside the network Forges · GitHub, Gitea pushes to Run branches only Slack · Linear · Notion written only by the outbox registries · docs allowlisted reads only Sizing · design target 20 concurrent Sandboxes and their runners (NFR-10), within a Postgres connection budget per deployable; EU regions unless an Operator says otherwise Hetzner Object Storage WAL archive · base backups · EU targets: RPO ≤ 15 min · RTO ≤ 4 h Outside Hetzner apps/dead-man CF Worker cron Pushover separate bucket encrypted Infisical backups, own creds offline copy the Operator's root- secret recovery copy Restore drill · monthly restores Postgres and Infisical; encrypted fields must decrypt with the restored keys WAL archive heartbeat backups copy
Separate failure domains by design. The secrets pool shares no database or host with control (AD-42), and a Tenant pool hosts only that Tenant (AD-23). Every value in this drawing is a design target.

What it costs v1 runs one secrets server without high availability, and a Sandbox snapshot lives only on its host, so losing a host loses its snapshots. The Event Store, the Run’s branches and replay survive; the design names both as v2 options rather than v1 promises.

All of it at once

  • AD-3
  • AD-5
  • AD-6
  • AD-11
  • AD-12
  • AD-26
  • AD-31
  • AD-33

The drawings above are still pictures of one mechanism each. The one below runs them together, as a scripted scenario: one Factory and three example Projects, one per stage — AgentEff building itself, a client project releasing, and a new project being planned. Messages move between mailboxes, every state change lands in the Event Store, model calls draw down each Project’s Budget, RootSupervisor restarts a crashed Worker, and the Audit Watcher freezes a subtree until the Operator lifts it.

Watch the obligations strip in each lane. A promise such as a ReplyOwed lives on the owing Agent’s stream, is opened in the same append as the message that caused it — before any model call — and closes only on a typed event, ReplyPosted or the Run’s RunFinished, never on an Agent’s say-so. The design target is an acknowledgement within 10 s, even from a sleeping Agent.

It is an illustration, not a recording. The Projects are examples, and the tenants, Agents, event names, positions, budget levels and restart limit are sample data, with the timing compressed. Select any actor to see what it owns, or a concept card to jump to the moment it happens. If the reader’s system asks for reduced motion, the scenario starts paused, and Step moves it one message at a time.

Illustrative scenario · example projects · sample data
00:00 / 01:50step 0 / 71
NOWThree Projects, one Factorysample dataAgentEff builds itself, a client project (example) is releasing, and a new project (example) is still being planned. Press Play, or Step through one message at a time.

Scroll sideways to see the whole drawing.

FACTORY · shared by every ProjectHUMANS · via Slack and ingressTOOLS · written only by the outboxFACTORY ENTITIESSquad Leads delegate; only Workersrun Sandboxes and push code.BUILDINGAgentEffexample run · the Factory’s own repo · dogfoodingBudget node (sample)DEV SQUADENTITIES · one writer eachTICKET PROCEDURE · run/AE-142BranchPull requestCIOBLIGATIONSRELEASINGClient project (example)tenant: client · sampleBudget node (sample)QA SQUADDEVOPS SQUADENTITIES · one writer eachRELEASE PROCEDURE · run/CP-88SuitePRCReleaseOBLIGATIONSPLANNINGNew project (example)tenant: new · pre-code · sampleBudget node (sample)PM SQUADENTITIES · one writer eachPRD TO BOOTSTRAP · run/NP-1DocsLinearRepositoryOBLIGATIONSOperatorClient OwnerSlackLinearGitHubNotioncapacity/modetopologyrun/AE-142ticket/AE-142run/CP-88knowledge/clientapproval/prodaudit/clientrun/NP-1prd/new-projectdocpage/prdapproval/prd22% spentplancodePRCI———none open47% spenttestsPRCstagingproductionsuite · running—staging: current · prod: currentnone open6% spentPRDepicsstoriesBootstrap———none openRunStarted#1 · run/CP-88SlotGranted#2 · capacity/modeAttemptStarted#3 · run/CP-88Lifecycle Grow → Sleep#4 · agent/w-21Lifecycle Grow → Sleep#5 · agent/ops-leadLifecycle Grow → Sleep#6 · agent/pm-leadLifecycle Grow → Sleep#7 · agent/directorRootSupervisor · Cluster Singleton · no LLMRootSupervisorCluster Singleton · no LLM0Awakebreaker w-07 0/3 · FreezeView e7Audit Watcher · LLM-free rule setAudit WatcherLLM-free rule set0AwakeDirector · one per FactoryDirectorone per Factory0SleepingModel Gateway · apps/model-gatewayModel Gatewayapps/model-gateway0AwakeEgress proxy · per-Sandbox allowlistEgress proxyper-Sandbox allowlist0AwakeOutbox · intent → call → resultOutboxintent → call → result0AwakeEvent Store · PostgreSQL · append-onlyEvent StorePostgreSQL · append-only0Awakedev-lead · Dev Squad Leaddev-leadDev Squad Lead0Groww-07 · Dev Workerw-07Dev Worker0SleepingSandboxw-08 · Dev Workerw-08not born yet0UnbornSandboxqa-lead · QA Squad Leadqa-leadQA Squad Lead0Groww-12 · QA Workerw-12QA Worker0WorkingSandboxops-lead · DevOps Squad Leadops-leadDevOps Squad Lead0Sleepingw-21 · DevOps Workerw-21DevOps Worker0SleepingSandboxpm-lead · PM Squad Leadpm-leadPM Squad Lead0Sleepingw-31 · PM Workerw-31PM Worker0SleepingSandboxw-32 · PM Workerw-32PM Worker0SleepingSandboxhead #7

EVENT LOG newest first · scenario time

  1. 00:00Client projectw-12 is running the client project’s acceptance suite in its Sandbox, as an Activity of run/CP-88.AD-6
  2. 00:00FactoryThe Director, pm-lead and the DevOps Squad are asleep. Sleeping Agents make no model calls.AD-11
STATESBornGrowAwakeWorkingSleepingCrashedRestartingFrozen

Born, Grow and Sleep are Lifecycle states (AD-11). Awake marks a Sleep → Grow on the next message, Working means an Activity is running (AD-6), and Crashed, Restarting and Frozen are what supervision and freeze do to an actor (AD-26, AD-33). A sleeping actor is dimmed.

MESSAGEStell, the sender does not waitask, the sender waitsreplyevent appended to the Event StoreAgent tree, parent to child
COLOUREntity messagemodel callHumantoolsupervision, alarm or refusalfreezeread from the Event Store
Every move is a message, and every change is an event. No Agent writes another’s stream or calls a tool or model directly: the outbox, the Model Gateway and RootSupervisor stand between each Agent and the world (AD-3, AD-5, AD-31, AD-33). The three Projects are examples; tenants, Agents, event names, positions, budget levels and the restart limit are sample data, and the timing is compressed.

What this does not show yet

None of this is running. The spine is final, and the build starts with the foundations: standing the Factory up and commanding it from Slack, then having it deploy itself to Hetzner. Everything after that is a plan, and is described here as one.

Writing on this site is meant to cover the mechanisms, what they cost, and where they turned out not to hold. This first post can only do the first two. The third needs code under the decisions, and a later post will say which of them held once there was.