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

# Change a Shape and publish it

> A Shape change is published, and existing entities still read and accept the new properties, or the person knows which stay on the old version.

Guide · shapes

> **Use when**
>
> A person wants a type, a property or a relationship added, changed or removed in a Shape that the project already has.

> **Looking for something else?**
>
> A new Shape for a new domain (recipe [create-a-shape](https://docs.pnbr.io/guides/create-a-shape.md)), or a public Shape you adopt as it is (card [Shape](https://docs.pnbr.io/concepts/shape.md)). Designing what the Shape should hold, which this recipe does not cover.

## Outcome

The Shape has a new published version with a message. Each entity that exists
follows the change and takes the new properties. Otherwise the person knows
that it stays on the old version, and why.

## Before you start

1. Know your credential: `penumbra whoami`. It prints the kind of credential,
   but not your role or a key's scopes. If you do not know the role, ask the
   person. An owner or an admin publishes. A
   project API key needs the scope `shapes.draft.publish`. Its publish waits
   for approval only if the project's policy holds `shape_change` (card rule
   [shape-draft.7](https://docs.pnbr.io/concepts/shape-draft.md#rule-shape-draft-7)).
2. Read the Shape and its versions: `penumbra shapes show <shape>` and
   `penumbra shapes log <shape> --limit 3`. Note the newest version and its message.
   If no Shape of the project fits the domain, do not bend one. Create a new
   Shape (recipe [create-a-shape](https://docs.pnbr.io/guides/create-a-shape.md)).
3. Ask what the person wants in their words. Write down each type, property or
   relationship that changes, and the value the new property should hold.
4. Count what exists: `penumbra graph counts`.

## Steps

1. Open an edit draft of the current version: `penumbra shapes edit <shape>`. `penumbra shapes fork <shape>` makes a separate
   Shape instead (card rule [shape-draft.1](https://docs.pnbr.io/concepts/shape-draft.md#rule-shape-draft-1)). MCP: in `execute`,
   `const shape = await penumbra.shapes.open({ name })` starts or continues the
   draft of that Shape.
   * Expect: `Draft <id> is open at revision 1`. Keep the draft id.

2. Pull the definition: `penumbra shapes pull <draft-id>`. MCP:
   `shape.read({ part: "model" })`.
   * Expect: `Wrote <name>.shape.json`.

3. Edit the file. Prefer changes that older entities follow: add an optional
   property or a type, widen an enum, or make a required property optional (card
   rule [shape.4](https://docs.pnbr.io/concepts/shape.md#rule-shape-4)). To rename, change `name` and keep the ref (card rule
   [shape.2](https://docs.pnbr.io/concepts/shape.md#rule-shape-2)), and expect older entities to stay behind (card rule [shape.5](https://docs.pnbr.io/concepts/shape.md#rule-shape-5)). Keep the old property when you can, and add the new one beside it.
   A new optional property goes into the `properties` list of its type:

   ```json theme={null}
   {
     "property_ref": "tier",
     "name": "tier",
     "schema": { "type": "string", "enum": ["gold", "silver"] },
     "required": false
   }
   ```

4. Push the file: `penumbra shapes push <name>.shape.json`. MCP:
   `shape.define({ types, relationships })` or `shape.modify([{ op, ... }])`.
   * Expect: `Synced draft <id> to <name>.shape.json: revision <n>`. On
     `revision_conflict`, pull again and reapply your edits (card rule
     [shape-draft.2](https://docs.pnbr.io/concepts/shape-draft.md#rule-shape-draft-2)).

5. Check the draft: `penumbra shapes check <draft-id>`. MCP:
   `shape.validate()`.
   * Expect: `Draft <id> is valid at revision <n>` and exit code 0. Fix each
     diagnostic, push and check again (card rule [shape-draft.4](https://docs.pnbr.io/concepts/shape-draft.md#rule-shape-draft-4)).

6. Read the diff: `penumbra shapes diff <draft-id>`. MCP:
   `shape.read({ part: "changes" })` lists the changes but no impact or bump, so
   use the CLI diff to judge them.
   * Expect: a list of changes, each with its impact, and a recommended bump.
     An added property shows `(creating)` and `minor`. A rename shows
     `(preserving)` and `patch`.
   * If every change is `(preserving)` or `(creating)` and nothing is renamed,
     older entities follow it. Go to step 7.
   * If any change is `(losing)` or `(breaking)`, or the bump is `major`, or
     something is renamed, stop. Follow problem [breaking-shape-change](https://docs.pnbr.io/troubleshooting/breaking-shape-change.md). Tell the
     person, and publish only after they choose.

7. Publish: `penumbra shapes publish <draft-id> -m "<what changes>"`. MCP:
   `shape.submit({ note })`. A held submit returns a pending value; follow it
   with `check_status`.
   * Expect, for a person's own session: `Published version <n> of Shape <id>.` and exit code 0.
   * If the policy holds `shape_change`, a key or a connected agent gets exit
     code 4 and `pending`. Give the person
     the approval URL and follow it with `penumbra operations wait <handle>`
     (problem [change-waits-for-approval](https://docs.pnbr.io/troubleshooting/change-waits-for-approval.md)). Nothing is published until it is
     approved.
   * When `wait` ends with `outcome: succeeded`, read the new version number
     from `penumbra shapes log <shape>`. The JSON that `wait` prints holds a
     different number.

8. Give the entities their new values. This works across a change that older
   entities follow. For each entity that needs the new property, read it with `penumbra context entity <entity-id> --json` and note
   `entity.revision`. The text output does not print the revision. Then run `penumbra graph update <entity-id> --revision <n> --properties '<every property, with the new one>'`. The update replaces the
   whole property map (card rule [entity.2](https://docs.pnbr.io/concepts/entity.md#rule-entity-2)). Use `penumbra graph update`, not
   a workspace: a workspace plans the change under the version of the entity,
   and refuses the new property (card rule [shape.6](https://docs.pnbr.io/concepts/shape.md#rule-shape-6)). MCP: `update_node` in
   the full catalog.
   * Expect: `updated node <id>` and `commit: <n>`. Read the entity again: its
     revision is one higher, and its `artifact_digest` is the newest.

9. To extract with the new version, start a new extraction (recipe
   [extract-files](https://docs.pnbr.io/guides/extract-files.md)). It opens its own workspace and reads the newest version
   (card rule [extraction.15](https://docs.pnbr.io/concepts/extraction.md#rule-extraction-15)). A workspace that already holds work in this
   Shape keeps its old version (card rule [shape.6](https://docs.pnbr.io/concepts/shape.md#rule-shape-6)). Penumbra does not extract Files
   again, and it does not recheck entities, after a publish. To find entities
   that no longer match their Files, use recipe [find-stale-entities](https://docs.pnbr.io/guides/find-stale-entities.md).

## Check

1. `penumbra shapes log <shape>` shows the new version first, with your message.
2. Run `penumbra context explore --shape-id <shape-id> --limit 200 --json`. For
   each entity that you updated in step 8, the `artifact_digest` under [shape](https://docs.pnbr.io/concepts/shape.md)
   equals the newest `artifact_digest` in `penumbra shapes log <shape> --json`.
   The explore output lists no properties, so run `penumbra context entity <entity-id> --json` and find the new value in `properties`.
3. For every other entity that stays on an older digest, you can say why. An
   additive change leaves it on the old version until an edit saves it under the
   new one, and it still reads. A breaking change leaves it behind, and the
   person knows.

## If it goes wrong

| You see | Go to |
| - | - |
| A push exits with code 3 and `revision_conflict` | card rule [shape-draft.2](https://docs.pnbr.io/concepts/shape-draft.md#rule-shape-draft-2) |
| A change marked `(losing)` or `(breaking)`, a `major` bump, or a rename | problem [breaking-shape-change](https://docs.pnbr.io/troubleshooting/breaking-shape-change.md) |
| The publish exits with code 4 and prints `pending` | problem [change-waits-for-approval](https://docs.pnbr.io/troubleshooting/change-waits-for-approval.md) |
| `forbidden` on the publish | card rule [shape.10](https://docs.pnbr.io/concepts/shape.md#rule-shape-10) |
| `draft_locked` on a push | card rule [shape-draft.8](https://docs.pnbr.io/concepts/shape-draft.md#rule-shape-draft-8) |
| `graph_plan_rejected` on `penumbra graph update` | problem [breaking-shape-change](https://docs.pnbr.io/troubleshooting/breaking-shape-change.md) |
| `missing_property_declaration` from `penumbra graph workspace validate` | card rule [shape.6](https://docs.pnbr.io/concepts/shape.md#rule-shape-6) |
| A View refuses with `view_republish_required` | problem [view-needs-republish](https://docs.pnbr.io/troubleshooting/view-needs-republish.md) |

## Worth offering

* Use recipe [write-better-descriptions](https://docs.pnbr.io/guides/write-better-descriptions.md) when agents confuse concepts or the descriptions are vague, redundant or conflicting.
* Offer to update the existing entities that should hold the new property.
* Offer to extract again from the Files that state the new property (recipe
  [extract-files](https://docs.pnbr.io/guides/extract-files.md)).
* If a View shows types of this Shape, offer to publish the View again (problem
  [view-needs-republish](https://docs.pnbr.io/troubleshooting/view-needs-republish.md)).

## Report

Tell the person:

* The version number that you published, and its message.
* Each change in the diff, and whether it was additive or breaking.
* How many entities you updated, and which still sit on an older version.
* What waits for approval, who must approve it, and the approval URL.
* That Penumbra did not extract Files again and did not recheck entities.

## Keep going

[Shape](https://docs.pnbr.io/concepts/shape.md) · [Shape draft](https://docs.pnbr.io/concepts/shape-draft.md) · [Entity](https://docs.pnbr.io/concepts/entity.md) · [Workspace](https://docs.pnbr.io/concepts/workspace.md) · [Approval](https://docs.pnbr.io/concepts/approval.md) · [A Shape change renamed or removed something](https://docs.pnbr.io/troubleshooting/breaking-shape-change.md) · [A change returned pending](https://docs.pnbr.io/troubleshooting/change-waits-for-approval.md) · [A View does not follow a Shape change until it is published again](https://docs.pnbr.io/troubleshooting/view-needs-republish.md) · [Extract entities from Files](https://docs.pnbr.io/guides/extract-files.md) · [Find entities that rest on an old version or a trashed File](https://docs.pnbr.io/guides/find-stale-entities.md) · [Create a Shape and try it on a File](https://docs.pnbr.io/guides/create-a-shape.md) · [Write better descriptions](https://docs.pnbr.io/guides/write-better-descriptions.md)

Penumbra Grammar 0.6.26


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.