CLI Capability Interface Contracts
Status: shipped
Date: 2026-02-28
Updated: 2026-02-28
Deciders: Joel Hooks
Supersedes: None
Related: ADR-0144 (hexagonal architecture), ADR-0163 (adaptive prompt architecture), ADR-0172 (agent mail adapter), ADR-0181 (AT Proto adapter track)
Context
SYSTEM.md defines platform capabilities as joelclaw CLI commands (otel, recall, mail, secrets, deploy, notify, heal, log). These are the agent-facing operational surface.
Today, capability behavior is split across direct command implementations, shell wrappers, and external tools. Some capabilities are fully implemented, some are missing, and some are spread across multiple commands. There is no single typed contract for capability inputs/outputs, no adapter registry, and no shared configuration loader for capability backends.
Current State (2026-02-28)
| Capability | In SYSTEM.md | Current CLI Surface | State | Current Backend Reality | Gap to Contract Target |
|---|---|---|---|---|---|
otel | ✅ | joelclaw otel {list,search,stats} | Implemented (phase 4) | Capability adapter typesense-otel via registry/runtime | Maintain output parity while adapter internals evolve |
recall | ✅ | joelclaw recall | Implemented (phase 4) | Capability adapter typesense-recall via registry/runtime | Maintain retrieval quality and rewrite observability under adapter path |
secrets | ✅ | joelclaw secrets {status,lease,revoke,audit,env} | Implemented (phase 1) | Capability adapter agent-secrets-cli wrapping existing backend | Expand adapter breadth as backend evolves |
mail | ✅ | joelclaw mail {status,register,send,inbox,reserve,release,...} | Implemented (phase 4) | Capability adapter mcp-agent-mail under registry/runtime (MailPort) | Keep adapter health/diagnostics explicit and deterministic |
deploy | ✅ | joelclaw deploy worker | Implemented (phase 3) | Capability adapter scripted-deploy orchestrating deterministic worker sync path | Expand adapter coverage beyond worker sync only if needed |
notify | ✅ | joelclaw notify send | Implemented (phase 1) | Capability adapter gateway-redis on canonical gateway queue path | Add more notify verbs only if needed |
heal | ✅ | joelclaw heal {list,run} | Implemented (phase 3) | Capability adapter runbook-heal over deterministic runbook engine | Keep legacy recover surface as compatibility path during migration |
log | ✅ | joelclaw log write | Implemented (phase 1) | Capability adapter slog-cli with structured envelope responses | Add read-side parity only if useful (logs remains read path) |
subscribe | ❌ (not listed) | joelclaw subscribe {list,add,remove,check,summary} | Implemented (phase 4) | Capability adapter redis-subscriptions under registry/runtime | Keep as non-SYSTEM extension capability and preserve existing UX |
Additional drift to address
joelclaw capabilitiesalready exists as a navigation catalog command. This ADR uses “capability” as an architectural contract concept; both must be tied to the same registry to avoid drift.- Existing command implementations are not consistently aligned with ADR-0144 extraction boundaries (thin CLI composition root + heavy logic in reusable adapters/packages).
- CLI runtime config is currently centered on
~/.config/system-bus.env(packages/cli/src/config.ts), not capability TOML.
Decision
1) Treat each capability as a typed port with named adapters
A capability is not only a command name; it is a stable contract that can be implemented by interchangeable adapters.
import { Effect, Schema } from "effect"
export interface CapabilityContext {
readonly cwd: string
readonly now: Date
readonly config: JoelclawCapabilitiesConfig
}
export interface CapabilityCommandSpec<TArgs, TResult> {
readonly summary: string
readonly argsSchema: Schema.Schema<TArgs>
readonly resultSchema: Schema.Schema<TResult>
}
export interface CapabilityPort<
TCommands extends Record<string, CapabilityCommandSpec<any, any>>,
> {
readonly capability: string
readonly adapter: string
readonly commands: TCommands
execute<K extends keyof TCommands>(
subcommand: K,
args: Schema.Schema.Type<TCommands[K]["argsSchema"]>,
context: CapabilityContext,
): Effect.Effect<
Schema.Schema.Type<TCommands[K]["resultSchema"]>,
CapabilityError
>
}
export interface CapabilityError {
readonly code: string
readonly message: string
readonly retriable: boolean
readonly fix?: string
}Contract implications:
- Subcommands are typed (no free-form
stringdispatch). - Args/results have runtime validation via schemas.
- Adapters return typed results; CLI envelope wrapping stays centralized.
2) Keep CLI as composition root (ADR-0144)
packages/cliremains thin command wiring + envelope formatting.- Capability adapter logic lives in dedicated
@joelclaw/*packages/modules. - No cross-package
../../src/*imports for adapter logic.
3) Standardize capability config with clear precedence
Capability adapter selection is configured in TOML with deterministic override order:
- CLI flags (highest)
- Environment variables
- Project
.joelclaw/config.toml - User
~/.joelclaw/config.toml - Built-in defaults
# ~/.joelclaw/config.toml
[capabilities.mail]
enabled = true
adapter = "mcp-agent-mail"
[capabilities.mail.adapters.mcp-agent-mail]
endpoint = "http://127.0.0.1:8765"
timeout_ms = 5000
[capabilities.secrets]
enabled = true
adapter = "agent-secrets"
[capabilities.notify]
enabled = true
adapter = "gateway-redis"4) Clarify command overlap semantics
log= write structured entries (joelclaw log write ...)logs= read/analyze runtime logs (joelclaw logs ...)notify= canonical alerting command;gateway pushremains low-level transport/debug surfacedeploy= canonical deployment trigger; existing specialized deploy commands become adapter-specific implementations
Phased Implementation Plan (value/effort ordered)
Phase 0 — Contract foundation (small, required first)
- Add capability contract types + adapter registry.
- Add TOML capability config loader with precedence + schema validation.
- Keep existing command behavior unchanged.
Exit criteria: registry + config loader exists; no command regressions.
Phase 1 — Fill SYSTEM.md command gaps with thin adapters (high value, low effort)
- Ship
joelclaw secrets(adapter wraps existingsecretsCLI behavior). - Ship
joelclaw log(adapter wrapsslog write/ canonical event path). - Ship
joelclaw notify(adapter wraps current gateway push flow).
Exit criteria: secrets, log, notify commands exist and return standard HATEOAS envelopes.
Phase 2 — Ship joelclaw mail (highest value missing capability)
- Implement
MailPort+mcp_agent_mailadapter (ADR-0172). - Subcommands:
register,send,read,reserve,release. - Add capability health check path for adapter reachability.
Exit criteria: role/system workflows can actually execute joelclaw mail commands end-to-end.
Phase 3 — Consolidate fragmented operations
- Implement canonical
joelclaw deploycapability (initial adapter: existing script/CLI workflows). - Implement canonical
joelclaw healcapability (compose existing recover/nas/inngest heal flows). - Keep legacy subcommands as compatibility aliases until migration completes.
Exit criteria: deploy/heal actions available behind single capability commands with explicit adapters.
Phase 4 — Migrate already-implemented capabilities behind registry
- Move existing
otel,recall,subscribecommands onto capability registry interfaces. - Keep command UX stable; swap internals to adapter architecture.
Exit criteria: all target capabilities execute through the same port/adapter path.
Phase 5 — Optional AT Protocol mail adapter (deferred)
- Add
atproto-pdsadapter only after parity tests with MCP adapter and lexicon stabilization. - No impact to CLI contract surface.
Exit criteria: adapter swap via config only, no command changes.
AT Protocol evolution path
Adapter swap remains a valid direction, but is explicitly deferred until prerequisites exist:
- stable mail lexicon + message/reservation mapping
- identity/auth strategy for multi-agent writes
- parity tests proving
mcp-agent-mailandatproto-pdsadapters satisfy the sameMailPortcontract
Until then, AT Proto remains future scope, not phase-1 delivery.
Implementation Update (2026-02-28)
- Phase 0 shipped: capability contract types, registry, and deterministic config precedence loader (
flags > env > project > user > defaults). - Phase 1 shipped:
joelclaw secrets,joelclaw log, andjoelclaw notifycommand groups with HATEOAS envelopes and adapter-backed execution. - Phase 2 + 4 shipped with ADR-0172 alignment:
joelclaw mailnow executes through capability registry/runtime viamcp-agent-mail(MailPort), not bespoke command internals. - Phase 3 shipped: canonical
joelclaw deployandjoelclaw healcommand roots now execute through capability adapters (scripted-deploy,runbook-heal). - Phase 4 shipped:
otel,recall, andsubscribecommand internals now execute through capability registry/runtime adapters (typesense-otel,typesense-recall,redis-subscriptions) while preserving command UX/envelopes. - Remaining ADR-0169 scope: none required for this ADR. Optional
atproto-pdsadapter work is now tracked separately in ADR-0181.
Consequences
- Capability contracts become concrete and testable (typed subcommands + schemas).
- SYSTEM.md capability claims become executable in prioritized phases.
- CLI remains a thin composition root aligned with ADR-0144.
- Adapter swapping becomes practical (mail now, AT Proto later) without command rewrites.
- Scope becomes deliverable: immediate value first, aspirational items clearly deferred.