Before and After
Five contrasts. Same scenario each time - the old way, then the holon way.
A function vs. a Capability
A function carries behavior. It knows how to do something; it doesn't know what it accepts, what it guarantees, or what it means. Everything outside it - callers, validators, AI agents, docs generators - has to read the source to understand it.
A Capability declares its input contract, its output shape, its execution
logic, and its intent. Pass style: 'diagonal' and TypeScript catches it before
execution. The schemas are data that exist at runtime: a validator checks against
them, a docs generator reads them, an AI agent understands each field through its
.describe(). The function's contract lived only in your head; the Capability's
contract is part of what it IS.
A complex system vs. a typed handle
Not every Capability maps input to a value. Some open something - a connection, a
session, a stream - and return a handle the caller uses to interact with
what's inside. The Capability holds the internal state; the caller gets typed
methods. A Transaction Capability keeps BEGIN, the connection acquire, and the
release inside itself, and hands back Query, Commit, Rollback. Forget to
commit, forget to release? Those paths live inside the Capability - the caller
can't misuse them by accident. The handle IS the contract.
An assertion test vs. an Intent
A Deno.test records a verification after the fact: "this ran, this matched." The
test is separate from what it tests - when the function moves, the import breaks.
An Intent inverts this: it's part of the Capability's definition.
CapabilityIntent('FormatText — title case', FormatText)
.WithInput({ text: 'hello world', style: 'title' })
.Expect(ExpectCapabilityResultEquals({ formatted: 'Hello World' }))
.Run();
The intent is a machine-readable declaration that travels with the entity. When an
AI agent reasons about FormatText, it reads the intents to learn what the entity
is supposed to do - not just what it happened to produce last run. You're not
writing tests to catch bugs; you're declaring the contract the holon makes to
every caller, in the same language as the code.
A service class vs. a Steward
As systems grow, behavior gets organized into service classes - objects that own related operations and hold dependencies through constructor injection. The class IS the coupling point: add an operation, touch the class; swap a dependency, touch the class; test an operation, test the whole dependency tree.
A Steward is the container; Operations are the mutable layer; the
Capabilities stay stable. Adding an Operation means adding an entry to
.Operations({}) - the existing Operations don't know about it. Each Operation
declares exactly what it needs (.Capabilities({}) for typed access,
.Services(async (ctx, ioc) => ...) for IoC-resolved dependencies). Nothing about
the parts changes when the composition changes.
A raw LLM call vs. an Agent
An LLM interaction is usually a raw fetch - no declared contract on the input,
no schema on the output, no governance on who can call it. To an AI reading your
codebase, it looks like any other fetch. An Agent gives the LLM interaction
the same structure as everything else: typed input validated before the call, a
handle contract (SendMessage, Close), an intent declaration, and governance
hooks via .Governance(). Compose it into an Operation with .Agents({}) and it
stays exactly the same entity.
What a holon carries
A function carries its logic. A service class carries its dependencies. A fetch
call carries its implementation. Every entity in @fathym/steward carries
something more:
- A Capability carries its input contract, output contract, execution logic, and intent - the same in every context.
- An Agent carries its input schema, its handle contract, and its intent. The LLM call, the credentials, the session live inside; outside is a typed input and a message-based handle.
- An Operation carries its Capabilities, Agents, and Services, and declares exactly what it needs. Change the Operation and the Capabilities don't change.
- A Steward holds its Operations, coordinates execution, and shares governance. The entities inside stay whole.
None of these collapse when composed. Each was whole before composition, and stays whole inside it. That's what "whole and part" means in code.
- Synaptic-Oriented Programming → - the practice of building this way