Chapter 3

Primitives & Harnesses

Know the vocabulary of what APM manages and how those primitives relate to agent harnesses.

Objective

After this chapter you can name the seven primitive types APM manages — instruction, prompt, skill, agent, plugin, MCP server, and hook — tell the three that look alike apart by who triggers them, explain that APM is built on the open AGENTS.md, SKILL.md, and MCP standards rather than replacing them, and describe how one source primitive compiles into each harness's native files. Naming the primitives is what makes a context declaration portable and a security decision governable in the chapters ahead.

Concept/Theory

You can't package what you can't name

In Chapter 1 the team's agent context was an undifferentiated pile — “an AI setup,” “some rules,” “that prompt.” You can't declare a pile in a manifest, pin it in a lockfile, or gate it in a policy. Before any of that, each item needs a type. That is the whole job of this chapter: hand you the nouns. Chapter 2 showed that names and namespaces are what tame ambiguity in package managers; here we give agent context its names.

APM's word for a typed unit is a primitive — “the atomic unit APM ships.” The supported kinds are instructions, skills, prompts, agents, hooks, commands, plugins, and MCP servers (glossary) — though “command” is a prompt as some harnesses project it rather than a separate thing to author, and the experimental canvas type is out of scope here, which leaves seven authorable types. A primitive is not the same as a package: a package is the unit of distribution — a directory with an apm.yml that can bundle many primitives — while a primitive is one typed unit inside it. And not every file is a primitive: only files under the recognised primitive layout are discovered, validated, and deployed; everything else is just repo content (glossary).

The primitive vocabulary

APM manages seven primitive types you'll meet daily. The point of the vocabulary is that they are not interchangeable: each has a different trigger, cost, and blast radius. Read the middle column carefully — who (or what) invokes it is the axis that keeps the types straight.

The seven primitive types APM manages, by trigger. Definitions condensed from APM's “what APM manages” table and primitive catalogue (What is APM?, primitives and targets).
Primitive What it is Who invokes it Problem it solves
Instruction Repository-scoped guardrails and coding standards, scoped by file glob Nobody — ambient, read on every turn (always-on) Persistent rules you never have to re-state
Prompt A parameterized, runnable workflow — “a callable program for an LLM” You — explicitly, by name (you run it) A repeatable multi-step task or checklist on demand
Skill Model-invocable know-how — a SKILL.md folder plus bundled resources The model — when a task matches its description (it reaches for it) Specialized capability loaded only when relevant
Agent A persona with its own scope, tools, and system prompt The user or orchestrator selects it as the acting persona A bounded specialist instead of one generalist
Plugin A bundle of the primitives above, packaged for one-shot install Installed as one unit, then unpacked into its primitives Shipping a set of primitives together
MCP server An external tool or data connection over Model Context Protocol The agent at runtime, when it calls that tool Governed access to outside tools and data
Hook A lifecycle handler that runs before or after agent tool calls The harness runtime, automatically at that event Deterministic automation around tool calls

Two more pairs are worth keeping straight. An agent is who is acting — a persona with its own tools and boundaries; a skill is how to do a task — a procedure the acting agent can pull in, and one agent can use many skills. And a plugin is not a new capability at all: it is a packaging format that ships several primitives together and is normalized back into those same typed primitives at install time (primitives and targets).

Built on standards, not instead of them

APM does not invent its file formats. It is “built on standards: AGENTS.md · Agent Skills / SKILL.md · MCP” (microsoft/apm README). Each standard is an independent, open format APM adopts for one slice of the vocabulary:

  • AGENTS.md — “a simple, open format for guiding coding agents… a dedicated, predictable place to provide context and instructions” that works across many agents (agents.md). APM's instructions compile into AGENTS.md-style files.
  • Agent Skills / SKILL.md — “a lightweight, open format for extending AI agent capabilities… a skill is a folder containing a SKILL.md file” loaded by progressive disclosure (agentskills.io). APM's skill primitive is this format.
  • MCP (Model Context Protocol) — “an open-source standard for connecting AI applications to external systems” (modelcontextprotocol.io). APM declares MCP servers against this protocol.

Standing on shared standards is what makes APM a manager rather than another silo. The standards define the file and protocol formats; APM adds the dependency layer on top — resolution, pinning, a lockfile, policy, and multi-harness compilation. So APM-produced context is readable by tools that never installed APM, and APM can consume primitives authored by people who never used it. Concretely, apm compile emits distributed AGENTS.md context files from your instructions — the open standard is the output, not a proprietary blob (What is APM?).

In APM

One source, many harnesses

A harness is the agent runtime that executes primitives — harnesses include GitHub Copilot, Claude Code, Cursor, Codex, Gemini, OpenCode, Windsurf, and Kiro, with Antigravity as an additional explicit-only target — and “each harness has its own primitive directory layout and file format” (glossary). A primitive, by contrast, is harness-agnostic. APM's central move is to sit between the two: you author one set of primitives under .apm/, and APM writes them into each harness's native location. It “does not invent a runtime format. It writes the files each tool already understands and stays out of the way at agent runtime” (What is APM?).

Because each harness has its own format, APM either writes a primitive natively (the harness reads it directly) or compiles it (APM transforms it into a shape the harness understands). The clearest proof is to install one source package into two harnesses and look at what lands on disk. The public microsoft/apm-sample-package ships four of the seven primitive types, so four rows follow — a real install, same source, zero edits, once with --target copilot and once with --target claude:

One source primitive set → two harnesses, two on-disk layouts — and even two different names. Observed from a real install of microsoft/apm-sample-package. apm v0.23.1
Source primitive (.apm/…) Copilot (.github/…) — native Claude Code (.claude/…) — compiled
instruction instructions/*.instructions.md .github/instructions/*.instructions.md .claude/rules/*.md — renamed to a rule
prompt prompts/*.prompt.md .github/prompts/*.prompt.md .claude/commands/*.md — a /command (mode key dropped)
agent agents/*.agent.md .github/agents/*.agent.md .claude/agents/*.md
skill skills/<name>/SKILL.md .agents/skills/<name>/SKILL.md .claude/skills/<name>/SKILL.md

Read across any row and portability stops being a slogan. The same source instruction becomes a Copilot .github/instructions/ file but is renamed to a Claude rule; the same prompt is a native Copilot prompt but a compiled Claude /command. (Skills for the Copilot family land in the cross-client .agents/skills/ home rather than a Copilot-only directory, which is why that row's left cell reads .agents/, not .github/.) The intent is identical everywhere; the file is whatever each harness natively reads.

Targets are the declaration; the harness is the runtime

You don't name a harness directly in the manifest — you list targets. A target is “the declaration… the manifest or CLI selector for which harnesses to compile for,” while “the harness is the runtime that consumes the compiled output” (glossary). Eight harnesses are configured as targets by default — copilot, claude, cursor, codex, gemini, opencode, windsurf, and kiro — which is why this book says “many harnesses” and lists them rather than promising every tool ever built (What is APM?). Selecting and pinning targets is Chapter 5's job; here it is enough to see the shape: one declaration, many native destinations.

Two of the book's four properties are already visible in this layer. Declaring context as typed primitives is the basis of Portability — one source set, compiled into every harness you target. And because each primitive is a named, typed unit, an organization can allow or deny it by category, which is what makes Governance discussable at all. The remaining two arrive downstream: the lockfile pins each deployed primitive by content hash — the basis of Reproducibility (Chapter 6) and of provenance (Chapter 8).

When to use / pitfalls

Which primitive for the job?

Distinct names prevent category errors. “Add a security review” is ambiguous until you say who triggers it: a persistent instruction that always warns about logging card data, a prompt the reviewer runs on a PR, a skill the model pulls in when it recognises a payment change, or an agent that owns review end to end. Each has a different cost and blast radius, so pick by trigger:

Choosing a primitive by who — or what — triggers it.
If you want… Reach for a… Because
a rule enforced every turn that no one has to remember instruction it's ambient and always on
a named checklist a human kicks off on a PR prompt you run it, explicitly, by name
know-how the model should pull in only when a task matches skill the model reaches for it, keeping context lean
a bounded specialist that owns a task end to end agent it's a persona with its own tools and scope
deterministic automation around every tool call hook the runtime fires it at a lifecycle event
a governed connection to an external tool or dataset MCP server it's declared and gated, run elsewhere
several of the above shipped as one installable unit plugin it's a packaging format, unpacked into primitives

Worked example

Meridian's context, finally typed

Here is Meridian's pile from Chapter 1, sorted into its primitive map. Nothing below is a manifest yet — it is the same inventory, now with a type and a trigger on every row. Read down the trigger column and the instruction/prompt/skill split snaps into focus: three of these are markdown, but one is always on, one you run, and one the model reaches for.

Meridian's primitive map — every scattered file from Chapter 1, now typed. A map, not yet a manifest. apm v0.23.1
# meridian-checkout, now TYPED:  source item  ->  primitive   (who triggers it)
#
checkout backend guardrails (use Money) -> instruction  nobody: read every turn (always-on)
fraud-review PR checklist               -> prompt       you: invoked by name, on demand
secure-review know-how                  -> skill        the model: reached for on payment diffs
API-architect persona                   -> agent        selected as the acting specialist
payment-sandbox tool connection         -> mcp server   the agent at runtime (declared + gated)
#
# not yet needed: plugin (no bundle to ship) . hook (no lifecycle automation)

That map is heading toward a manifest, but guard the distinction: it is not yet a manifest. The shape it will take groups the five rows into two dependency buckets — apm for primitives and mcp for MCP servers — plus the harness targets to build for. Everything below is illustrative; Chapter 4 validates the real syntax and pins each source, and Chapter 5 installs it.

Conceptual only — the same map as a manifest shape, showing the two dependency buckets and harness targets. Not validated until Chapter 4. apm v0.23.1
name: meridian-checkout-agent-context
targets: [copilot, claude, cursor]   # 3 of APM's 8 default harness targets
dependencies:
  apm: []              # primitives -- declared for real in Chapter 4:
    #  - <checkout-domain instruction>
    #  - <fraud-review prompt>
    #  - <secure-review skill>
    #  - <api-architect agent>
  mcp: []              # MCP servers -- declared + gated here, the server runs elsewhere:
    #  - <payment-sandbox>

Recap & next

Recap

  • A primitive is APM's atomic, typed unit of agent context. Naming the primitives is the prerequisite for everything else — you can't package, pin, or govern what you can't name.
  • There are seven types. The three that look alike differ by trigger: instruction = always on; prompt = you run it; skill = the model reaches for it. An agent is who acts; a plugin is packaging; an MCP server is an external tool; a hook is lifecycle automation.
  • APM is built on open standards — AGENTS.md, SKILL.md, and MCP — not a replacement for them; apm compile emits distributed AGENTS.md files.
  • One source primitive compiles into each harness's native files: an instruction becomes a Copilot instruction or a Claude rule; a prompt becomes a Copilot prompt or a Claude /command. Compiled is not byte-identical; parity across the eight default harnesses is high but bounded (native / compiled / unsupported).
  • Targets are the declaration you list; the harness is the runtime that consumes the output.
  • Typed primitives underpin Portability and make Governance a concrete checklist. Meridian typed its pile into a primitive map — the seed of the next chapter's manifest, but not yet a manifest.

Next

You now have the nouns and a typed map. Chapter 4 — The Manifest: apm.yml turns Meridian's primitive map into a valid apm.yml: real dependency syntax, pinned git sources, and the two buckets (apm and mcp) filled in for the first time.