fa644f1826
Stand up the executus/run kernel foundation, decoupled from mort: - runengine.go: the shared run-loop scaffolding (MergeCancellation, CleanupContextTimeout, RunFinalizer/FireFinalizers, RunStateAccessor) moved from mort. The accessor's *skillaudit.Writer dependency is inverted to a narrow run.RunTally interface (TokenStats + ToolCallsCount) — the kernel reads live tallies without importing the audit battery. - submit.go: the legacy submit-capture compat tool (stdlib + majordomo/llm). - agent.go: RunnableAgent DTO — the kernel's view of "a thing to run" (tier, prompt, caps, palette, phases, critic config). The persona Agent and saved Skill will LOWER into this DTO so the kernel never imports a noun battery. This is the spine of the agentexec.Run(*agents.Agent) inversion. run/ builds with only majordomo + executus/tool. The executor merge (agentexec+skillexec -> run.Executor) and the nil-safe run.Ports (Audit/Critic/Budget/Checkpointer/PaletteSource) are the next P2 block. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
109 lines
5.3 KiB
Markdown
109 lines
5.3 KiB
Markdown
# 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`.
|
||
|
||
[majordomo]: https://gitea.stevedudenhoeffer.com/steve/majordomo
|
||
|
||
## 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/ run-loop mechanics (cancel-merge, finalizers, [P2 wip]
|
||
RunStateAccessor via RunTally seam, submit,
|
||
progress bridge) + RunnableAgent DTO done;
|
||
executor merge + nil-safe run.Ports next [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 ✓]
|
||
(convar->config.Source; UsageSink/TraceSink seams; GenerateWith[T]
|
||
structured output — no separate structured/ pkg)
|
||
llmmeta/ shared meta-LLM helper over model/ [P1 ✓]
|
||
compact/ context compactor (WithCompactor hook) [P2]
|
||
tools/{web,net,store,compose,meta,comms} generic tools [P3]
|
||
|
||
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.sum` must not
|
||
contain gorm/redis/discordgo/sqlite/gin.
|
||
- No `core/*` package imports a `battery/*` package.
|
||
- Standard Go gates: `go build`, `go vet`, `go test -race`, `go mod tidy` clean.
|
||
|
||
## 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`, this `CLAUDE.md`, and `examples/` 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.
|