Morphic Agent Framework

What Morphic Is

A vocabulary for describing the agents that do the work, and the software they are working on.

Here is a problem I actually had.

I had a directory of Markdown files describing AI agents and skills. Each one had YAML frontmatter and a body of instructions. Two of them needed the same three paragraphs about how to read the project's specification. So I copied the paragraphs. Then there were five. Then I changed one of them and forgot the other four, and an agent spent an afternoon confidently doing the wrong thing based on instructions I had personally fixed a week earlier.

Markdown files have no inheritance. Psy does.

use "@psy/morphic"

export skill ProjectContext:
    description: Read the project before changing it.
    when: Use before making any edit.

    prompt:
        Read the project's README, its configuration, and the code nearest to
        the change before making any edit.

export skill ImplementChange extends ProjectContext:
    description: Implement a change end to end.
    when: Use whenever a change alters behaviour.

    prompt:
        + super

        Add or update tests for every behavioural change.
psy build

.claude/skills/implement-change/SKILL.md:

---
name: implement-change
description: Implement a change end to end. Use whenever a change alters behaviour.
---

# ImplementChange

Read the project's README, its configuration, and the code nearest to the change
before making any edit.

Add or update tests for every behavioural change.

Two real Markdown files, with the shared paragraph appearing in both, from one source. Change it once and both regenerate.

That is the first half of Morphic. There is a second half, and it turned out to matter more.

The second problem

Deduplicating instructions makes your agents consistent. It does not make them correct.

An agent that has been told exactly how you like your tests written, and nothing about what you are building, will write beautifully-tested code for the wrong program. It will invent an architecture. It will name things plausibly and wrongly. It will "fix" a deliberate design decision because nothing told it the decision was deliberate. Then you review it, explain the architecture in the review, and the next task starts from zero again.

The missing input is not better instructions. It is a description of the software — one that lives in the repository, is checked by a compiler, and gets handed to the agent automatically when it is relevant.

export component LayerStack:
    description: The ordered stack of layers in a document.

    responsibilities:
        - Maintain layer order and z-index
        - Apply blend modes and opacity

    invariants:
        - Order is stable across save and load
        - The renderer never mutates it
---
name: layer-stack
description: "Component: The ordered stack of layers in a document."
---

# LayerStack

## Responsibilities

- Maintain layer order and z-index
- Apply blend modes and opacity

## Invariants

- Order is stable across save and load
- The renderer never mutates it

## Authority

This describes the product as it is meant to work. Where the implementation
disagrees, treat this as correct unless the task is explicitly to change it.

That is the second half, and it is what Specifying Software is about.

Two families, one vocabulary

Morphic adds eight declaration keywords in two families, and nothing else.

How to work. Imperative. Generated as instructions.

KeywordIs
skilla reusable instruction other declarations build on
agentan autonomous worker with a toolset and a model
commandsomething a person invokes by name

What is being built. Declarative. Generated as reference material.

KeywordDescribes
conceptan idea you have to understand, starting with the program itself
domaina logical area, and what belongs to it
featurea specific behaviour
componenta reusable unit of implementation
referencecross-cutting material: voice, naming, visual language

They are separate because they change for different reasons. Skills change when your process changes. Definitions change when the product changes. Keep them in one bucket and every product change churns your instructions, and every process change churns your architecture docs.

And they are linked in exactly one direction: a skill, agent or command names the definitions it needs with context.

export skill ImplementRendering:
    description: Implement a change in the renderer.
    when: Use when changing anything under the rendering domain.

    context:
        - Rendering
        - LayerStack

    prompt:
        Keep mutation out of the renderer.
---
name: implement-rendering
description: Implement a change in the renderer. Use when changing anything under the rendering domain.
---

# ImplementRendering

Keep mutation out of the renderer.

## Context

Consult these definitions of how the product works:

- `rendering` (Rendering)
- `layer-stack` (LayerStack)

An agent that picks up that skill is handed the canvas rules. It does not go looking, and it does not guess.

Psy knows nothing about any of this

Worth being clear, because it is the reason the design holds together.

Morphic is an ordinary Psy framework, built exactly the way Your Own Keywords described. The keywords exist only because @psy/morphic exports abstract specs that expose them with as, and only in modules that use it. The core compiler has never heard of a skill or an agent, and could not be made to care about one.

All eight are ordinary specs. They inherit. They compose prompts with + super. They carry any extra properties you invent.

Turning it on

// src/skills/ReviewDiff.psy
use "@psy/morphic"

export skill ReviewDiff:
    description: Review a diff for correctness and clarity.
    when: Use before approving any change.

    prompt:
        Read the whole diff before commenting on any part of it.
// psy.config.ts
import { defineConfig } from "@psy/config";
import { morphic } from "@psy/morphic";
import { claude } from "@psy/adapter-claude";

export default defineConfig({
    source: ["./src"],
    frameworks: [morphic()],
    targets: [claude({ output: ".claude" })],
});

Morphic ships as one package with two halves that travel together:

HalfWhat it isWhat it does
Psypsy/framework.psydeclares the keywords and what each accepts
TypeScriptmorphic()enforces the rules a type cannot express

You want both. The Psy half so the keywords exist; the TypeScript half so psy check catches an empty prompt.

use "@psy/morphic" resolves the package's Psy entry point. A standalone psy binary carries its own copy, so this works in a project that has installed nothing at all.

Taking only half

use "@psy/morphic" activates all eight keywords. To take only the working half:

use "@psy/morphic/psy/framework"

Rarely worth it — unused keywords cost nothing — but it is there.

Making the vocabulary yours

use activates the keywords as they ship. To attach project-wide instructions to everything, import the specs instead and extend them:

// src/framework.psy
import { Agent as MorphicAgent, Skill as MorphicSkill } from "@psy/morphic"

export abstract spec Skill extends MorphicSkill as skill:
    contract:
        The specification in `spec/` is authoritative. Read it before changing
        behaviour.

export abstract spec Agent extends MorphicAgent as agent:
    contract:
        Run the verification commands, and report honestly what passed.

Your modules then use "./framework" rather than the package. Every skill now inherits contract, which becomes a section in every generated artifact, from one edit.

The same trick adds requirements:

export abstract spec Component extends MorphicComponent as component:
    abstract owner: string

    testing:
        Every component ships with a rendering test and an interaction test.

Every component must now declare an owner, and inherits a Testing section. This is the single highest-leverage thing in Morphic and it is worth doing on day one.

One warning: do not use both the package and a local module that re-exposes the same keyword. Activating one keyword twice is PSY3009.

What Morphic does not do

It does not generate anything. Not one byte. Turning a Morphic model into files is an adapter's job, which is deliberate: a different adapter could render these same declarations for a completely different platform without changing a line of Psy.

This section

  1. What Morphic Is — you are here.
  2. Skills, Agents And Commands — the working half, property by property.
  3. Specifying Software — how to describe your software so an agent can build it.
  4. concept — the ideas you have to understand.
  5. domain — a region of the problem, and what belongs to it.
  6. feature — behaviour, described from outside.
  7. component — a unit of implementation, and its invariants.
  8. reference — the rules that apply everywhere.
  9. What Gets Generated — sections, validation, and the exact file mapping.
  10. Shipping A Plugin — packaging it all as a Claude Code plugin.

Next: Skills, Agents And Commands

On this page