The tool registry core (registry, permission model, Invocation, gated-tool wrapper, ssrf guard, hmac, encryption, argcoerce, helpers, rootrun, session_tools, webhook_rate_limit) had zero mort coupling — it imports only majordomo/llm + x/crypto/hkdf — so it moves verbatim with a package rename (skilltools -> tool). All same-package tests came along and pass; the SSRF, gated-wrapper, encryption and output-pattern invariants are re-anchored here. majordomo re-enters the module graph (now pinned to the latest, incl. the front-loaded-output fix). model/ + llmmeta + structured follow next. Docs: CLAUDE.md now requires README/examples to stay in sync with changes in the same commit; CI skips docs/example-only pushes via paths-ignore. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5.1 KiB
executus — developer & agent guide
⚠️ This project is vibe-coded (AI-authored, human-steered). See
README.md.
executus is a batteries-included base for LLM agent harnesses, layered
strictly above majordomo. majordomo is the lean substrate (agent loop, llm
types, providers, media, parse/failover/tiering). executus is the opinionated
layer majordomo deliberately omits. executus requires no majordomo changes —
it decorates llm.Model and wraps majordomo/agent.Agent.
North star
A brand-new project imports executus, does a little setup, and is most of the way
to agentic capabilities. The mechanism is one shipped default per seam:
executus.New() (once the runtime lands) is agentic with zero host wiring; the
same builder lets a serious host swap each default for its own implementation and
register its own tools.
Two consumers define the envelope:
- mort (heavy) — Discord, mortbux, media, MySQL/GORM, DB-backed convar config, saved skills, audit, scheduling, run-critic.
- gadfly (light) — a CI PR-reviewer Docker image, env-var configured, running an N-models × M-lenses structured-output swarm. Needs model fleet, lanes, bounded runs, structured output, fan-out, a few read tools — and none of the batteries.
That spread is why executus is tiered: a light host imports core only; a heavy host opts into batteries.
Module & layering
One module gitea.stevedudenhoeffer.com/steve/executus, go.mod = majordomo +
stdlib only (no gorm/redis/discordgo/cgo). A second nested module
contrib/store carries the SQLite dependency so the core never inherits it.
CORE (majordomo + stdlib):
config/ ConfigSource seam (+ env default) [P0 ✓]
lane/ bounded fair-share worker pool [P0 ✓]
fanout/ programmatic N×M swarm [P0 ✓]
deliver/ output egress seam (+ Discard/Stdout) [P0 ✓]
identity/ caller identity seams [P0 ✓]
run/ progress bridge now; the executor kernel + [P0 partial]
nil-safe Ports + RunnableAgent later [P2]
dispatchguard/ loop/depth/fan-out caps [P0 ✓]
pendingattach/ attachment dedupe [P0 ✓]
tool/ registry + 3-stage permissions + ssrf [P1 ✓]
model/ config-driven tier resolution over majordomo [P1]
llmmeta/ shared meta-LLM helper (moves with model/) [P1]
compact/ context compactor (WithCompactor hook) [P2]
tools/{web,net,store,compose,meta,comms} generic tools [P3]
structured/ Generate[T] convenience over majordomo [P1]
BATTERIES (opt-in siblings, each nil-safe + a default):
persona/ Agent noun + AgentStore seam + yml loader [P4]
skill/ rich Skill + SkillStore seam + toml loader [P4]
audit/ run-trace Sink (+ Noop/Slog) [P4]
critic/ two-tier timeout state machine + Escalator [P4]
schedule/ cron runner cores [P4]
checkpoint/ durable resume seam [P4]
budget/ rolling-window tracker (+ NoOp) [P4]
contrib/store/ SECOND module (+ modernc.org/sqlite): [P4]
in-memory + pure-Go SQLite impls of every *Store seam
The one architectural move
The kernel must import no battery. In mort today, agentexec imports
agents, agentcritic, and skillaudit directly — those three up-pointing edges
get inverted into nil-safe run.Ports interfaces (PaletteSource, Critic,
Audit) plus a RunnableAgent DTO. Everything else is wide-but-shallow
repackaging.
Invariants (enforced in CI)
- The core module builds with majordomo + stdlib only.
go.summust not contain gorm/redis/discordgo/sqlite/gin. - No
core/*package imports abattery/*package. - Standard Go gates:
go build,go vet,go test -race,go mod tidyclean.
Extraction roadmap
P0 module + zero-coupling moves + core seams (this) → P1 tool registry + model →
P2 run kernel + Ports inversion → P3 generic tools + defaults → P4 persona/skill
redesign + batteries + SQLite store → P5 gadfly on core (light-tier canary) → P6
rewire mort + tag v0.1.0. The mort-side rewrite reuses mort's existing
mort_*_adapters.go wall as the host adapter layer.
Conventions
- Keep
README.md, thisCLAUDE.md, andexamples/in sync with every change, in the SAME commit. No aspirational docs: when you add/rename a package, change a seam or a default, or alter the public API, update the docs and the relevant example so they always reflect reality (mirrors majordomo's house rule). The status markers in the tier map above must track what's actually landed. - Mirror majordomo's house style: gofmt; check errors immediately and wrap with
fmt.Errorf("...: %w", err);// Why:comments where rationale isn't obvious; hermetic tests (majordomo's fake provider; no network in the default suite). - Every seam is an interface with a nil-safe accessor and a shipped default.
- Keep the core seam surface small and stable — push churn into tools and host adapters, not core interfaces.