Layer 2 — mag-composition
Packages: @mag/composition-core, @mag/composition-prompt,
@mag/composition-knowledge, @mag/composition-capability,
@mag/composition-conversation.
In plain language
Between the substrate and the product sits a layer that executes nothing and renders nothing. It defines what a reusable component is — a prompt, a knowledge collection, a tool, a review — how it is versioned and governed, and how it binds to the substrate primitive that does the work. It also defines the grammar for composing components into a Capability: generate, route, act, review. Studio assembles these; the product's resolver compiles them; Magpie runs them. Because the layer belongs to no single application, the same components and the same grammar serve every Solution.
The mechanism
One base model (INV-COMP-01). Every component, of every family,
conforms to the Composition Component Base Model: identity and
classification (family, kind), ownership, lifecycle (draft → published → deprecated → archived), version, tags, runtime resolution, relationships,
governance and audit. A published version is immutable; a change is a new
version. New families and kinds are added by extending the enumerations
through an ADR; the base shape does not fork per type.
Components reference; they never re-implement (INV-COMP-02, -04). A
component's resolutionStrategy and runtimeBinding are one typed
discriminated union whose binding shapes mirror exactly what the
corresponding Magpie primitive accepts today. A Prompt Component holds
metadata and a pointer or version reference — never prompt text (the
validator rejects any body-like field). A Knowledge Component is an
aggregate over one or more sources plus retrieval parameters. A inline
strategy means a Layer-2-only declarative object with no substrate binding.
Zero substrate dependency (INV-COMP-03). The packages import nothing
from @magpie/*. Magpie concepts are cited, not imported; a Magpie surface
change is a governed update here, never silent drift. Dependency direction
is one-way and lint-enforced.
The Capability authoring model (ADR-0006, -0007). A
CapabilityDefinition declares ordered inputs, a CapabilityFlow of steps,
outputs, publication and access metadata, and an evaluation namespace. Steps
bind data symbolically — source: "input" or source: "step" with a path —
and never construct the substrate's projection machinery.
The composition grammar. Four step roles:
| Role | What it composes | Compiles to |
|---|---|---|
generate |
a Prompt Component, a model, optional Knowledge | a prompt node |
route |
a deterministic verdict → next-step map, default required | a condition node (ADR-0013) |
tool |
a platform tool with dynamic inputs and static options | a tool node (ADR-0015) |
review |
a human decision with allowed roles | a human-review node |
Routing is deterministic: it consumes only a structured verdict from a
prior step, never a transcript, and an unmatched verdict routes somewhere
explicit. A routed capability declares one canonical output by listing
its branch steps in preference order (first-present, ADR-0014), so readers
never learn about branches.
Evaluation is a namespace on the version (ADR-0009 … 0012).
evaluation: { primarySuiteId?, freshnessDays?, mode?: "advisory" | "required" } is version-scoped, frozen on publish, declarative and
consumer-resolved: the layer validates the shape and attaches no behaviour.
"blocking" was removed because it added no meaning beyond "required"
and smuggled policy into an enum; stronger governance is a new explicit
field, never a new mode value.
The resolution contract (ADR-0008). The resolver — the only code that
knows both @mag/* and @magpie/* — lives downstream in the product. This
layer specifies what it must satisfy: prompt component → prompt node with
model precedence rules; knowledge binding → query-time retrieval feeding a
named prompt variable; outputs → the terminal's output builder. The contract
exists before more families are built so no family accrues divergent
resolution behaviour.
Conversation components (@mag/composition-conversation) carry the
presentation contract for conversational Solutions — the validated envelope
of prose, presentation directives and state updates that a client such as
the Front Door renders — with fail-closed vocabulary validation.
Design notes
- Why a separate repository and version line (ADR-0001). Lockstep is right for the substrate because its packages are one coherent release; it is the wrong fit for a layer with a different cadence and consumer set.
- Why a layer that executes nothing exists (constitution). "Magpie executes. mag-composition composes. Studio authors. Applications consume." Identity, metadata, versioning and discovery of reusable components need one owner that is neither the runtime nor any single app.
- Why routing is a first-class step (ADR-0013). "Do not hide routing in compiler side channels. The composition grammar must be capable of expressing a routed workflow directly." The authored artifact must be the complete truth of a capability's control flow.
- Why the tool step arrived when it did (ADR-0015). The runtime had
been tool-capable for some time, but no authored Solution could reach
it; the Studio write path refused six reserved
tool.*identities rather than let one silently execute as a prompt. The grammar was added so tools could be lifted one governed tool at a time.
Sources
mag-composition/README.mdanddocs/architecture-constitution.md(INV-COMP-01…08).mag-composition/docs/adr/0001…0015.mag-composition/packages/composition-capability/src/,composition-core/src/.