Fathym
Menu

Creating Holons

The definitive guide to building a holon - whether you're adding a Capability or defining a brand-new holon type. Everything here follows from one idea: every holon is whole and part, so it shares one contract and one shape.

Holon taxonomy - what should I build?

Three categories, by primary role:

  • Behavioral (Agent, Capability, Governance, Workflow) - own their execution. Each has its own Runner and extends InvokableEntityBase.
  • Execution context (Steward) - hosts behavioral entities and dispatches via .Run(); owns its own builder.
  • Strategy (Operation) - composes behavioral holons; no Runner, no Governance of its own.

A special case: orchestrating holons are behavioral holons that also own an inner Steward (with clone-on-write + Configure/Start) - that's how vaults and workbenches in @fathym/power-ai are built. See Advanced Patterns.

The universal contract

Every holon shares a six-method contract - the expression of Self-Assertion and Integration in the API:

MethodTendencyPurpose
.Execute(fn)S-AThe holon owns how it runs
.Governance(policy)INTParticipation in oversight
.Services(factory)INTIoC dependency resolution
.Build()INTModule production (Engine calls only)
.Input(schema)Input contract (or a domain-named alias)
.Output(schema)Output contract (or a domain-named alias)

Both S-A and INT are always required - a holon that only asserts can't compose; a holon that only integrates has no identity.

Builder / Runner / Engine

The same pattern across every holon type - only the names differ:

HolonFactoryRunnerVerb
CapabilityCapability()CapabilityRunner().Execute()
AgentAgent()AgentRunner().Setup()
GovernanceGovernancePolicy()GovernancePolicyRunner().Evaluate()
WorkflowWorkflow()WorkflowRunner().Run()

Golden rule: .Build() is called ONLY by the Engine, never externally. Always pass the builder (a Buildable) to the Runner. See Builder, Runner, Engine.

Code style

  • Public methods: TitleCase (Execute, Build).
  • Private/protected: camelCase (executeFn, stepBuilders).
  • Prefer protected over private; always write the explicit public modifier; never use a _ prefix.

Carry-forward generics

Builder methods return the concrete subclass type with updated generics (via as unknown as SubclassBuilder<Updated>), and accumulate types by intersection (TServices & S) - never replacing. New composition methods store builders directly, not invokers. This is the type system encoding holonic participation.

The four sub-holon composability types

Every holon that can be composed publishes four types (named from the CHILD holon), all declared in its *Module.ts:

  1. *Invoker - the callable shape a caller holds at runtime.
  2. *InvokerMap - a map of name → Invoker.
  3. *BuilderMap - a structural map of name → Buildable.
  4. *TypeMap<T> - the mapping function (builders → invokers), applied in context only.

Services - accumulation and mocking

.Services() calls merge (spread-on-write) onto the previous - so services accumulate across calls. At test time, real services + a mock override layer let you swap dependencies. Operations receive (ctx, ioc) - not just ioc - so resolution can be context-scoped.

Testing with intents

Declare intent as the natural completion of building a holon - see Testing with Intents for the full IDD pattern (CapabilityIntentSuite, mocked services, compile-time contract guards).

Creating a new holon type - the 8-component template

When you define a brand-new holon type, you write eight pieces: (1) a factory function, (2) a Builder class extending InvokableEntityBase with carry-forward generics, (3) a Module interface extending InvokableEntityModule, (4) a Context interface (readonly, domain vocabulary), (5) an Engine (lazy build → validate I/O → resolve services → govern → execute), (6) a Runner (domain verb), (7) an IntentRuntime, and (8) Intent + Suite. Field names match across all four layers (builder method, module field, context field, intent method).

Anti-pattern checklist

Before you ship a holon, audit: code style (TitleCase/camelCase, explicit public, no _); generics carry forward by intersection, no explicit generics at call sites; .Build() only in the Engine; Module extends InvokableEntityModule; Context fields readonly; composition keys TitleCase with all four sub-holon types declared; no aggregation across boundaries (Operations have no Governance of their own); IntentSuite present.

Canonical file/folder structure

{holontypes}/
├── .exports.ts          (root barrel - re-exports the 4 sub-barrels)
├── types/               (*Module.ts with the 4 composability types; *TypeMaps.ts helpers)
├── fluent/              (factory, *Builder, *Runner, *RunnerBuilder, built-ins)
├── services/            (*Engine.ts)
└── intents/             (*Intent, *IntentRuntime, *IntentSuite, expectations/)

Each .exports.ts re-exports only its immediate siblings; the root re-exports the four sub-barrels. The package entry maps ./{holontypes}./src/{holontypes}/.exports.ts.


On this page