> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pnbr.io/llms.txt
> Use this file to discover all available pages before exploring further.

# How a shape governs your graph

> The data model behind a shape, what adherence modes do to capture and extraction, and why the same shape governs capture, extraction, and retrieval.

A shape is the contract for a slice of your graph. It defines the entity types
that can exist, the properties each type carries, and the relationships that
connect them. The same shape that decides what you can capture also decides what
extraction is allowed to produce and what retrieval reads back. When you author a
shape in the [Workbench](/shapes/overview), you are setting the rules for all
three at once.

## The data model

A shape is a nested model. Read it top to bottom:

| Level            | What it is                                                           | Example                             |
| ---------------- | -------------------------------------------------------------------- | ----------------------------------- |
| **Shape**        | The contract for a domain slice.                                     | `research`                          |
| **Entity type**  | A kind of node the shape allows.                                     | `finding`, `source`                 |
| **Property**     | A field on an entity type, with a kind and required/optional status. | `content` (required), `kind`        |
| **Relationship** | A typed edge between two entity types.                               | `evidence` — `SUPPORTS` — `finding` |

When you [materialize](/shapes/materialize) the shape, it becomes the contract
the runtime writes against for that project. Under `strict` adherence, an entity
that does not match a type in the shape, or a property the type does not
declare, has nowhere to land; `loose` adherence treats the model as a guide
rather than a hard boundary (see below).

```text theme={null}
shape
  └─ entity type
       ├─ property
       └─ relationship → entity type
```

## Adherence modes

Every shape carries an adherence mode that controls how strictly capture and
extraction must conform to the model. You set it on the shape; it applies to
everything written through that shape.

<Tabs>
  <Tab title="strict">
    Only declared types, properties, and relationships are accepted. A capture
    or extraction that introduces an unknown type, an undeclared property, or a
    missing required field is rejected rather than written.

    Use strict when the graph feeds something downstream that depends on a fixed
    schema — typed accessors, exports, reporting — and you want guarantees over
    coverage.
  </Tab>

  <Tab title="loose">
    The declared model is the guide, not a hard boundary. Capture and extraction
    aim for the shape but tolerate additional properties and partial matches,
    so source material that does not fit cleanly still produces entities.

    Use loose when you are exploring a domain or ingesting messy material and
    would rather keep an imperfect entity than drop it.
  </Tab>
</Tabs>

<Note>
  Adherence is a property of the shape, not of a single call. To change how
  conformant writes must be, change the shape's mode and re-materialize.
</Note>

## One shape, three jobs

The reason a shape is worth authoring carefully is that it is reused at every
stage of the loop. Define a type once and it governs:

| Stage          | What the shape decides                                                                          | SDK surface                          |
| -------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------ |
| **Capture**    | Which entity types you can stage, and which properties they carry.                              | [`pb.capture`](/sdk/capture-extract) |
| **Extraction** | What an LLM is allowed to pull out of a source — coerced into the shape's types and properties. | [`pb.extract`](/sdk/capture-extract) |
| **Retrieval**  | What comes back, since results are typed entities matching the shape.                           | [`pb.search`](/sdk/search)           |

This is why the modeling work pays off in practice. If a type is missing a
property, extraction cannot capture that field no matter how clearly it appears in
the source — there is nowhere to put it. If a relationship is not declared,
neither capture nor extraction can draw that edge, so traversal later will not
find it. The shape is the ceiling on what your graph can know.

<Tip>
  Before you extract a new kind of source, check that the shape already has the
  types and properties you expect to find. Authoring the model first is cheaper
  than re-extracting after you notice the gap.
</Tip>

## Starter shapes ship with the platform

You do not start from nothing. Penumbra ships a curated kit of starter shapes
ready to use, including two you will reach for early. They do not appear in
`pb.shapes.list()` — discover them with `pb.shapes.starters()`.

### The `memory` shape

The default shape behind [`pb.memory`](/sdk/memory). It defines a single entity
type, `memory`, for agent working memory.

| Property         | Notes                                                                       |
| ---------------- | --------------------------------------------------------------------------- |
| `content`        | Required. The memory text.                                                  |
| `kind`           | One of `preference`, `decision`, `fact`, `lesson`, `observation`, `signal`. |
| `scope`          | One of `agent`, `commons`, `project`.                                       |
| `date_observed`  | When the memory was formed.                                                 |
| `source_context` | Where it came from.                                                         |

Its relationships connect a memory to what it is about and to what replaces it:

| Relationship    | Meaning                                     |
| --------------- | ------------------------------------------- |
| `ABOUT`         | Links a memory to the entity it concerns.   |
| `SUPERSEDED_BY` | Links a memory to the one that replaces it. |

To capture memory specific to your domain, fork this shape and add your own
properties — see [Custom memory layer](/shapes/fork-a-variant).

### The `research` shape

A starting model for research work, with the entity types `inquiry`, `source`,
`evidence`, `finding`, `open_question`, and `research_note`. Use it as-is to
structure investigation, or open it in the Workbench and extend it.

## Where a shape's entities live

A shape governs structure; [planes](/concepts/planes) govern role. The two are
orthogonal — the same shape's entities can sit on different planes depending on
how they were written.

| Plane        | What lives there                                                         |
| ------------ | ------------------------------------------------------------------------ |
| **Semantic** | The default canonical surface. Capture and extraction land here.         |
| **Memory**   | Where [`pb.memory`](/sdk/memory) writes and reads; recall defaults here. |
| **Archival** | Retired memory and history, kept out of the active surface.              |
| **Meta**     | Hidden internal infrastructure. You do not write to it.                  |

In practice: when you `pb.capture` or `pb.extract` through a shape, the entities
land on the **semantic** plane. When you write through `pb.memory`, the `memory`
shape's entities land on the **memory** plane, and recall reads them back from
there by default. Same data model, different plane, different default visibility.

## Next

<Columns cols={2}>
  <Card title="Author a shape" icon="pen-ruler" href="/shapes/authoring">
    Define types, properties, and relationships in the Workbench.
  </Card>

  <Card title="Capture and extract" icon="arrow-down-to-line" href="/sdk/capture-extract">
    Put knowledge into a shape-governed graph with the SDK.
  </Card>
</Columns>
