Lifts the 'an agent uses a SKILL.md pack' concept out of a host and into the
harness:
- run.Ports.SkillPacks (SkillPackActivator) — nil-safe port; the executor folds
a loaded agent's pack catalog into the system prompt and adds a skill_use
loader tool to the toolbox (uses the existing ra.SystemPrompt + toolbox seams)
- run.RunnableAgent.SkillPacks + persona.Agent.SkillPacks (+ skill_packs YAML,
extends-inherit, ToRunnable) — the Agent noun is now pack-aware
- skillpack.Activator — the battery's default port impl (resolve names → packs →
catalog + skill_use), with a per-run BundleStager factory the host plumbs;
satisfies the port structurally (no import of run)
- agentbuiltins: ships gifsmith, a portable focused GIF/MP4 render agent that
uses the gif pack — references tool/tier/pack NAMES only, no host coupling
A host now wires run.Ports.SkillPacks instead of carrying its own activation
glue. Tests: Activator resolution + gifsmith loads through persona→RunnableAgent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The headline P4 piece (clean redesign): the Agent persona noun, decoupled from
its Discord shell.
- agent.go/storage.go/builtin_loader.go moved from mort's pkg/logic/agents; the
Storage seam drops the Discord CommandBindingStorage embedding (a host
concern). The host-entangled files (commands, chatbot_provider, command-
binding dispatcher, personalization, system) stay in mort.
- runnable.go: Agent.ToRunnable() lowers a persona into run.RunnableAgent — the
bridge that lets run.Executor run a persona without importing this battery
(the inversion of agentexec.Run(*agents.Agent)).
- memory.go: NewMemory() — zero-dep in-process persona Storage (all 11 CRUD +
trigger-query methods).
Tests: ToRunnable field/phase mapping; Memory round-trip. CI invariant: core
imports ZERO from persona.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>