designing-entities
Designing entities
Guided, 5-phase workflow for designing and implementing one Electric Agents entity. Infers the coordination pattern from the user's description, reviews the design against universal and pattern-specific checks, and writes a single entity file once approved.
When to use
- User is starting a new entity in their app (
registry.define(...)/registerXxx(registry)). - User asks "how do I build an agent that..." / "I want an entity that...".
Do not use when editing an existing entity, querying entity streams (see the entity-stream-queries skill), or changing the runtime itself.
Workflow
The five phases are strict — do not skip, merge, or reorder. Reference files are loaded on demand only when the phase below says so.
1. Elicit
Ask the developer one open question:
Describe the entity you want to build. What should it do? Who/what does it interact with? What triggers it?
Skip this prompt if the opening message already contains a clear description.
2. Clarify
Load references/pattern-triggers.md. Match the description against the trigger table. If one pattern fits clearly, skip ahead. Otherwise, ask one disambiguation question per message — focused on narrowing the coordination shape (spawns / parallel vs sequential / fixed vs dynamic specialists / shared state / observes / LLM vs orchestration-only). Stop as soon as the pattern and shape are unambiguous. Typical count: 2–4 questions. Never ask a canned full list.
3. Propose pattern and design
State the inferred pattern and the reasoning:
Inferred pattern: <name>
Why: <trigger phrases> + <structural signals>
Load references/patterns/<selected>.md. If runtime details are needed, read the authoritative docs on demand (see "Canonical material" below). Then present the design outline in chat:
- Entity type name +
description - Coordination pattern (or "single-agent — no coordination")
creationSchema(if spawn args are expected) — field names + typesstatecollections — names, primary keys, row shapeinboxSchemas(if messages are typed)outputSchemas(if the entity produces typed output events)- Handler outline —
firstWakeinit, wake-type branches, agent config, spawn/observe calls- Key
ctxproperties:ctx.db.actions.*(insert/update/delete),ctx.db.collections.*(get/toArray),ctx.tags/ctx.setTag(key, value)/ctx.removeTag(key),ctx.events(Array of ChangeEvent from observed sources) - Observation sources:
entity(url),cron(expression),tagged({ match: ... })— import from@electric-ax/agents-runtime ctx.send(url, payload, { type?, afterMs? })—afterMsdelays delivery
- Key
- Built-in agents referenced (e.g.
worker), if any registerXxx(registry)factory wrapping it
Ask: "Design look right? Any changes before we run the review checks?" Wait for the answer. Revise if requested, then re-present the outline.
4. Review (loop until approved)
Load references/review-checklist.md. The pattern file from step 3 is already loaded. Apply both checklists mechanically against the design. Report:
Universal checks:
✓ Handler signature correct
✓ ...ctx.electricTools spread into tools array
✗ Spawn-once guard missing — child IDs collide on re-wake
N/A creationSchema — no spawn args
Pattern-specific (<name>):
✗ State machine transitions not defined
✓ Parallel spawn loop uses deterministic IDs
Proposed fixes:
- Add state.children collection + spawn-once guard
- Add state.status with transitions idle → mapping → reducing → idle
Apply these fixes? Anything to override?
Iterate: developer requests changes → revise design → re-run both checklists → report again. Loop until the developer explicitly approves ("looks good, write the file" or equivalent).
5. Implement
Confirm the output path — default entities/<type-name>.ts, or whatever the developer specifies. Write exactly one file using this shape:
import type { EntityRegistry } from "@electric-ax/agents-runtime"
import { z } from "zod/v4"
// schemas (creationSchema, inboxSchemas, collection schemas) go here
export function register<Name>(registry: EntityRegistry) {
registry.define("<type>", {
description: "...",
creationSchema,
inboxSchemas,
state: { ... },
async handler(ctx, wake) { ... },
})
}
After writing, tell the developer:
Wire it in by adding
register<Name>(registry)to your registry composition file (typicallyentities/registry.tsorserver.ts).
Do not patch the developer's registry file automatically. Stop — skill is done.
Invariants
- One question per message in phase 2.
- No files written before phase 5.
- Phase 4 loops until the developer explicitly approves.
- Reference files are loaded on demand, never upfront.
- The skill writes exactly one entity file. Multi-entity systems are designed by running the skill once per entity.
Canonical material (read on demand, do not preload)
How to access docs: paths below are relative routes (e.g.
/docs/reference/handler-context). If the file is available locally atelectric-agents-docs/docs/, read it from disk. Otherwise fetch fromhttps://durable-agents-docs.netlify.app/+ the route (e.g.https://durable-agents-docs.netlify.app/docs/reference/handler-context).
Runtime API reference:
/docs/reference/handler-context— thectxAPI (db, state, spawn, observe, send, agent, electricTools, firstWake, sleep, tags, setTag, removeTag, events)./docs/reference/wake-event— wake event types and theWakeconfiguration object./docs/reference/entity-definition— fullEntityDefinitioninterface./docs/reference/built-in-collections— the 17 built-in collections ondb.collections.*./docs/reference/state-collection-proxy—ctx.db.actions.*(insert/update/delete) andctx.db.collections.*(get/toArray) API./docs/reference/shared-state-handle—mkdb/observe(db(...))./docs/reference/agent-config—useAgent,AgentHandle.run(),testResponses./docs/reference/agent-tool— custom tool interface./docs/reference/entity-handle— whatctx.spawn/ctx.observereturn.
Usage guides:
/docs/usage/writing-handlers— handler lifecycle and re-entrancy./docs/usage/waking-entities— how wakes are produced and consumed; authoritative mental model forwake.type(only"message_received"and"wake")./docs/usage/defining-entities— registry and collection declaration patterns.
Built-in agent types:
/docs/entities/agents/horton— the dev-server's built-in assistant (chat, research, code, dispatch). Useful as a reference for custom single-agent entities./docs/entities/agents/worker— the built-in sandboxed subagent. Required spawn args:{ systemPrompt, tools }wheretoolsis a non-empty subset ofWorkerToolName. Workers do not receivectx.electricTools(least-privilege).
Pattern descriptions:
/docs/entities/patterns/<name>— one per pattern.
Canonical example implementations:
examples/durable-agents-playground/src/coordination/{manager-worker,pipeline,map-reduce,dispatcher}.tsexamples/durable-agents-playground/src/blackboard/{debate,wiki,peer-review,trading-floor}.tsexamples/durable-agents-playground/src/reactive/{monitor,summarizer,guardian}.tsexamples/durable-agents-playground/src/standalone/assistant.tsexamples/durable-agents-playground/src/workers/worker.ts— the playground registers its ownworkertype that acceptssharedState/builtinTools. When a real app spawns"worker"it gets the server's built-in least-privilege worker instead; adjust pattern examples accordingly.
Each references/patterns/<name>.md points at the specific canonical example for that pattern.