feat: foundations — canonical types, Parse grammar, env DSNs, health, chains
Phase 1 of the majordomo build: - llm/ canonical contract (messages, parts, tools, capabilities, streaming, Model/Provider, error classification) - health/ clock-injected tracker (threshold bench, exponential capped cooldown, reset-on-success) - root Registry + Parse (verbatim model ids, inline recursive alias expansion with cycle detection, chain dedup), LLM_* env-DSN providers (go-llm parity: lazy fallback + eager LoadEnv), health-aware chain executor behind the Model interface - provider/fake scriptable test provider; hermetic test suite incl. the trailing-thinking chain and foreman:// env loading - ADRs 0001-0008, CLAUDE.md, README (honest matrix), CI workflow, docs/phase-1-design.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# ADR-0001: Package layout — canonical types in a leaf `llm` package, root re-exports
|
||||
|
||||
**Status:** Accepted — 2026-06-10
|
||||
|
||||
## Context
|
||||
|
||||
Provider implementations (openai, anthropic, google, ollama/foreman) must share
|
||||
the canonical types (Message, Request, Response, Capabilities, Model, Provider).
|
||||
If those types lived in the root `majordomo` package, the root could not also
|
||||
register built-in providers (root → provider/openai → root is an import cycle).
|
||||
go-llm solved this with a `v2/provider` leaf package; the kickoff sketch puts
|
||||
the Provider interface in `provider/provider.go` and the message types at root,
|
||||
which recreates the cycle.
|
||||
|
||||
## Decision
|
||||
|
||||
- All canonical contract types live in the leaf package
|
||||
`majordomo/llm` (Message, Part, Request, Response, Option, Tool, Toolbox,
|
||||
Capabilities, Stream, Model, Provider, error classification). It imports
|
||||
nothing else in the module.
|
||||
- The root `majordomo` package re-exports every canonical type via type
|
||||
aliases (plus constructor/option wrappers), so consumers write
|
||||
`majordomo.Request`, `majordomo.UserText(...)` and rarely import `llm`.
|
||||
- The root owns assembly: Registry, Parse, env-DSN loading, the chain
|
||||
executor, and (from Phase 3) registration of real provider clients.
|
||||
- The planned `resolve/` package is folded into the root: the grammar needs
|
||||
registry state (aliases, providers, env fallback) at every expansion step,
|
||||
and a callback interface between two packages bought nothing but
|
||||
indirection.
|
||||
- `health/`, `media/`, `provider/<impl>/`, `provider/fake/`, `agent/`, and
|
||||
`skill/` are subpackages importing `llm` (and never each other, except
|
||||
agent → skill).
|
||||
|
||||
## Consequences
|
||||
|
||||
- No import cycles; new providers are additive subpackages.
|
||||
- Consumers get the flat one-import API the kickoff sketches.
|
||||
- Type aliases (not wrappers) mean zero conversion cost and full
|
||||
interchangeability between `majordomo.X` and `llm.X`.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Everything in root.** No cycles only if providers also live in root —
|
||||
a single giant package. Rejected.
|
||||
- **Self-registering providers via package init() side effects.** Hides
|
||||
wiring, breaks multi-registry isolation, surprises tests. Rejected.
|
||||
Reference in New Issue
Block a user