Morphic Agent Framework

feature

A specific behaviour the product exhibits, described from outside.

export feature PenTool:
    description: Draw and edit bezier paths.

    behaviour:
        Click places an anchor. Dragging while placing shapes the handles.
        Clicking the first anchor closes the path.

A feature is what the product does, described the way a user would experience it.

It is the only definition kind written from outside the codebase looking in. Everything else — concepts, domains, components — describes structure. A feature describes behaviour, and specifically the behaviour somebody decided on rather than the behaviour that fell out.

behaviourstring, required

What happens, in terms of what the user does.

The discipline is: no implementation nouns. No classes, no functions, no services, no events. If a sentence mentions a component, it belongs in the component, not here.

# No. This is a component's responsibilities wearing a feature's hat.
behaviour:
    PenTool registers with the ToolPalette and pushes AnchorCommands onto the
    CommandStack, which the renderer picks up on the next frame.

# Yes. This is what somebody using the product would tell you happens.
behaviour:
    Click places an anchor. Dragging while placing shapes the handles.
    Clicking the first anchor closes the path.

The second one is also the one that stays true when you rewrite the tool system.

Write the decided parts. If the behaviour is obvious from the name — "Zoom In makes things bigger" — you are padding, and padding in a normative document is worse than silence.

Which features to write

Write a feature when the behaviour was decided, not when it is obvious.

The signal is: there was a choice, and somebody made it, and a reasonable person would have made a different one. Undo coalescing. What happens on a failed export. Whether autosave overwrites. Those need definitions, because an agent implementing around them will otherwise guess — and guess plausibly, and guess wrong.

"Zoom in" does not need a definition. "Zoom in, and the zoom is anchored on the pointer rather than the canvas centre, because that is what people expect from maps" does.

Examples

A tool with edge cases

use "@psy/morphic"

export feature PenTool:
    description: Draw and edit bezier paths.

    behaviour:
        Click places an anchor. Dragging while placing shapes the handles.
        Clicking the first anchor closes the path.

        Once a path is closed, further clicks start a new path rather than
        editing the closed one. Editing requires selecting it first.

    edgeCases:
        - Escape abandons the in-progress path entirely. It is not undoable, because it was never committed.
        - A path with a single anchor is discarded on deselect rather than left as a stray point.
        - Dragging with no prior click does nothing. It is not a drag-to-draw gesture.

    constraints:
        - Every completed path is one undo step, not one per anchor
---
name: pen-tool
description: "Feature: Draw and edit bezier paths."
---

# PenTool

## Behaviour

Click places an anchor. Dragging while placing shapes the handles. Clicking the
first anchor closes the path.

Once a path is closed, further clicks start a new path rather than editing the
closed one. Editing requires selecting it first.

## Edge Cases

- Escape abandons the in-progress path entirely. It is not undoable, because it
  was never committed.
- A path with a single anchor is discarded on deselect rather than left as a
  stray point.
- Dragging with no prior click does nothing. It is not a drag-to-draw gesture.

## Constraints

- Every completed path is one undo step, not one per anchor

## 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.

edgeCases is where most of a feature's value lives. The main behaviour is usually guessable; the edges never are, and they are exactly what gets implemented wrong.

A feature with states

export feature Autosave:
    description: Preserve unsaved work without the user asking.

    behaviour:
        Five seconds after the last edit, the document is written to a
        recovery file beside the original. The recovery file is deleted on a
        successful explicit save, and on a clean quit.

        The user is never told this is happening. If it fails, they are not
        told that either — a failed autosave is logged and retried, because an
        alert about a background operation the user did not request is worse
        than the risk it is warning about.

    states:
        - Clean — no recovery file, document matches disk
        - Dirty — edits since last save, recovery file current
        - Recovering — a recovery file was found at launch

    constraints:
        - Autosave never writes to the user's file. Only the recovery file.
        - Recovery is offered once. Declining it deletes the file.

Note the second paragraph of behaviour. It states a decision and its reason, which is what stops somebody helpfully adding an error toast six months later.

A feature family, by inheritance

Features are ordinary specs, so shared behaviour is written once:

export feature DrawingTool:
    description: Behaviour common to every drawing tool.

    behaviour:
        Activating a tool deactivates the previous one and commits any
        in-progress work. Escape always cancels without committing.

    constraints:
        - One completed gesture is one undo step

export feature PencilTool extends DrawingTool:
    description: Draw freehand strokes.

    behaviour:
        + super

        Dragging draws a stroke that follows the pointer. Stroke width follows
        pressure where the input device reports it.

export feature EraserTool extends DrawingTool:
    description: Remove parts of a stroke.

    behaviour:
        + super

        Dragging erases within the current layer only. It never erases across
        layers, even where they overlap visually.
---
name: pencil-tool
description: "Feature: Draw freehand strokes."
---

# PencilTool

## Behaviour

Activating a tool deactivates the previous one and commits any in-progress work.
Escape always cancels without committing.

Dragging draws a stroke that follows the pointer. Stroke width follows pressure
where the input device reports it.

## Constraints

- One completed gesture is one undo step

## 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.

PencilTool.behaviour is both paragraphs, joined by a blank line, and its constraints are inherited whole. Change the cancellation rule once and every tool's generated artifact updates. See Composing.

A backend feature

Features are not only for user interfaces. Anything with externally-observable behaviour qualifies:

export feature UsageMetering:
    description: Count what a customer used, for billing.

    behaviour:
        Every billable action emits a usage record with a customer, a meter, a
        quantity and an idempotency key. Records are aggregated hourly.

        Late-arriving records are accepted for 48 hours and land in the period
        they were emitted in, not the period they arrived in.

    constraints:
        - A duplicate idempotency key is dropped silently, not an error
        - Usage records are immutable once written

    edgeCases:
        - A record arriving after its period is invoiced becomes an adjustment on the next invoice. Invoices are never reissued.

Extra properties that earn their place

PropertyUse it for
edgeCasesthe decided behaviour at the boundaries
constraintsrules the behaviour must not violate
stateswhen the feature is a state machine and the states have names
acceptancehow you would know it works
formats, limits, …whatever this specific feature has

feature or component?

They frequently describe the same code from two sides, and that is fine — you often want both.

featurecomponent
Point of viewoutside, a userinside, a maintainer
Sayswhat happenswhat it is responsible for
Stays true whenthe implementation is rewrittenthe behaviour is tweaked
Named likean action or capabilitya noun

Export the feature says a user picks a format and a path and gets a file. ExportPipeline the component says it owns format negotiation and never mutates the document. Different readers, different failure modes, both worth having.

When not to write one

  • For obvious behaviour. Padding in a normative document costs more than it gives.
  • For unbuilt behaviour. A definition is normative — write a feature for something you intend to build and an agent will treat it as true and "fix" the code to match. If it is not built and not being built now, it is an issue.
  • As a spec document. A feature is the decided shape, not a full acceptance suite. If it is longer than a screen, some of it is edge cases and some of it belongs in tests.

How it renders

---
name: pen-tool
description: "Feature: Draw and edit bezier paths."
---

# PenTool

## Behaviour

Click places an anchor. Dragging while placing shapes the handles. Clicking the
first anchor closes the path.

## Edge Cases

- Escape abandons the in-progress path entirely. It is not undoable, because it
  was never committed.
- A path with a single anchor is discarded on deselect rather than left as a
  stray point.

## Constraints

- Every completed path is one undo step, not one per anchor

## 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.

Next: component

On this page