ADR-007: Shared Plan Package vs Per-Agent Types
Status: Accepted
Date: 2026-05-09
Context
Plan specifications are central to Embercore’s workflow. Multiple components need to parse, validate, and operate on plan data:
- Athena generates plans from natural-language briefs
- Hermes executes plans step by step
- Hestia persists plans to SQLite
- plan.TopoSort determines execution order
- CLI commands (
plan,run,status) read and display plans
Initially, plan types lived inside agents/athena/plan.go since Athena was the first agent built. When Hermes was added and needed to reference the same types, a decision was needed: should Hermes import from Athena, or should shared types move to a dedicated package?
Decision
We maintain a shared plan/ package (packages/engine/plan/) that defines the canonical plan types, alongside Athena’s own plan types for generation-specific concerns.
Shared package (plan/plan.go)
type Spec struct {
Name string `yaml:"name"`
Description string `yaml:"description"`
Input Input `yaml:"input"`
Steps []Step `yaml:"steps"`
}
type Input struct { /* ... */ }
type Step struct {
Name string `yaml:"name"`
Agent string `yaml:"agent"`
Prompt string `yaml:"prompt"`
DependsOn []string `yaml:"depends_on"`
Checkpoint bool `yaml:"checkpoint"`
}
Plus shared operations:
Parse([]byte) (*Spec, error)— YAML deserializationValidate(*Spec) error— structural validationTopoSort([]Step) ([][]Step, error)— Kahn’s algorithm for execution layers (plan/toposort.go)
Athena-local types (agents/athena/plan.go)
type PlanSpec struct { /* generation-specific fields */ }
type InputSpec struct { /* ... */ }
type StepSpec struct { /* ... */ }
type PlanOpts struct {
Name string
Workflow string
Agents []string
MaxSteps int
Persist bool
}
These types handle Athena-specific concerns: LLM prompt construction, agent validation, YAML marshaling of generated plans, and circular dependency detection before handing off to the shared plan.Spec.
Current state
Both type sets exist:
plan.Spec/plan.Step— consumed by Hermes, Hestia, CLI, TopoSortathena.PlanSpec/athena.StepSpec— used during plan generation
This is an organic evolution: Athena was built first with its own types, then shared types were extracted when Hermes needed them.
Future direction
Consolidate toward the shared plan/ package:
- Have Athena generate
plan.Specdirectly (or convert internally) - Remove duplicate type definitions from
agents/athena/plan.go - All consumers import from
plan/only
Consequences
Benefits:
- Hermes, Hestia, and CLI don’t depend on the
athenapackage — clean dependency graph plan/package is self-contained with parsing, validation, and topological sort- Athena retains flexibility for generation-specific fields without polluting the shared spec
Trade-offs:
- Two similar-but-different type hierarchies (
plan.Specvsathena.PlanSpec) can confuse contributors - Conversion between Athena’s types and shared types adds mapping code
- The “two type” state is intentionally temporary — consolidation is planned
Related decisions: