Skip to main content
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), or a public Shape you adopt as it is (card Shape). 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).
  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).
  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). 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). To rename, change name and keep the ref (card rule shape.2), and expect older entities to stay behind (card 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:
  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).
  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).
  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. 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). 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). 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). 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). It opens its own workspace and reads the newest version (card rule extraction.15). A workspace that already holds work in this Shape keeps its old version (card 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.

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

Worth offering

  • Use recipe write-better-descriptions 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).
  • If a View shows types of this Shape, offer to publish the View again (problem view-needs-republish).

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 · Shape draft · Entity · Workspace · Approval · A Shape change renamed or removed something · A change returned pending · A View does not follow a Shape change until it is published again · Extract entities from Files · Find entities that rest on an old version or a trashed File · Create a Shape and try it on a File · Write better descriptions