Guide · graph
Use when
Two entities in the Graph are the same thing, and you have decided which one to keep.
Looking for something else?
Deciding whether two entities are the same thing (problem Two entities have the same name). A repeat that is still only an item in a workspace (problem An extraction found an entity the Graph already has).
Outcome
One entity, the kept one, holds every relationship and property that either entity held, with their citations. The other entity is deleted, and you can restore it.Before you start
- Confirm that the two entities are one thing (problem same-name-two-entities). Do not go on if you are unsure.
- Choose the entity to keep. Prefer the one with more relationships and citations, or the one that people already use. Write down both ids and both revisions.
- Know your credential:
penumbra whoami. A delete and a submit from an API key or a connected agent wait for approval (card rules approval.2 and workspace.10). One workspace holds both, so one approval covers them. - Use one credential for the whole recipe (card rule workspace.6).
- Choose a request id for the submit, such as
--request-id merge-<extra-id>-1(problem reply-lost).
Steps
- Read both entities. Write down the properties, the cited Files and every relationship of the extra entity.
- CLI:
penumbra context entity <kept-id> --jsonandpenumbra context entity <extra-id> --json. Use--relationship-limit 100and the cursor it prints for more. - MCP:
readwith the referenceentity:<id>for each. It shows the properties, the first 50 connections and the citations. - Program:
penumbra.domain.types.<Type>.get(id)for each, andentity.related(relationship, neighborType, {direction, limit, cursor})for each relationship name thatgrammarlists for the domain. Each result carriesedge.id. - Expect: for each relationship, its type, its direction, the id of the other end and its properties.
- CLI:
- List the relationships of the extra entity in both directions.
- CLI:
penumbra graph edges --node <extra-id> --direction both --limit 200. - MCP:
traverse_graphwithfrom: ["<extra-id>"]anddirection: "both"(full catalog). - Program:
entity.related(...)withdirection: "outgoing"and again with"incoming", as in step 1. - Expect: one line for each relationship. A relationship whose other end is the kept entity is not moved. It is deleted with the extra entity (card rule relationship.6).
- CLI:
- List the relationships of the kept entity the same way. Cross out from your list each relationship that the kept entity already has, with the same type, direction and other end.
- Open a workspace with a new name (card rule workspace.1).
- CLI:
penumbra graph workspace open "merge <extra-id>". - MCP:
workspace_open(full catalog). - Program:
penumbra.workspaces.open({name}).
- CLI:
- Stage each relationship that is left on your list, from or to the kept entity. Name the other end by its id (card rule relationship.3). Copy the type, the direction and the properties. Add
evidencewith citations you can support (card rule relationship.8).- CLI: put one line in a file for each relationship, such as
{"type":"WORKS_AT","from":"<kept-id>","to":"<other-id>","properties":{}}. Runpenumbra graph workspace capture "<workspace>" <file>. - MCP:
capturewithrelationshipsand anidempotency_key(full catalog). - Program:
penumbra.domain.stage({workspace, relationships: [{type, from, to}]}). It sets no properties and no citations. Use the CLI when the relationship has them. - Expect:
stagedand one line for each relationship, naming the ids.
- CLI: put one line in a file for each relationship, such as
- If the extra entity holds properties or citations that the kept entity lacks, check out the kept entity and stage them (card rule entity.2).
- CLI:
penumbra graph workspace hydrate "<workspace>" --node <kept-id>. Then write one line{"handle":"<handle>","changes":{"patch":{"properties":{"<name>":"<value>"}},"evidence":[{"file_id":"<id>","version_id":"<id>","content_digest":"<digest>"}]}}into a file. Take the three values from step 1. Runpenumbra graph workspace modify "<workspace>" <file>. - MCP:
workspace_hydrateandworkspace_modify(full catalog). Use a citation token fromreadasevidence: [{"citation": "<token>"}]. - Program:
w.hydrate({nodes: ["<kept-id>"]}), thenw.modify({edit: [{handle, changes: {patch: {properties}, evidence: [{citation}]}}]}). The patch merges into the properties of a checked-out entity. - Expect: the patch names only the properties the kept entity lacks. Skip this step if there are none.
- CLI:
- Check out the extra entity for deletion.
- CLI:
penumbra graph workspace hydrate "<workspace>" --node <extra-id> --intent delete. - MCP:
workspace_hydratewithnodesandintent: "delete"(full catalog). - Program:
w.hydrate({nodes: ["<extra-id>"], intent: "delete"}). - Expect: one item with
intent=delete.
- CLI:
- Validate the workspace (card rule workspace.3).
- CLI:
penumbra graph workspace validate "<workspace>". - MCP:
workspace_validate. - Program:
w.validate(). - Expect:
valid: yesand one deletion. Fix each line that starts witherror, then validate again.
- CLI:
- Tell the person which entity this deletes and which relationships move. Then submit as recipe review-and-submit says, with the revision that validate printed.
- CLI:
penumbra graph workspace submit "<workspace>" --revision <n> --request-id <key>. - MCP:
workspace_submitwithexpected_revision(full catalog). - Program:
w.submitReviewed({expectedRevision}). If the result ispending, follow it withpenumbra.actionStatus(id). - Expect:
submission: applied. With an API key or a connected agent, expect exit code 4 andoutcome: pending(problem change-waits-for-approval).
- CLI:
Check
- Run
penumbra graph edges --node <kept-id> --direction both. Each relationship on your list from steps 2 and 3 must appear with the same type and other end. - Run
penumbra graph get <kept-id>. Each property you carried over must show. - Run
penumbra context entity <kept-id> --json. The Files you cited in step 6 must be listed. MCP:readwithentity:<kept-id>shows the sources cited for its current revision. - Run
penumbra graph get <extra-id>. The output must have adeleted:line. MCP:readwithentity:<extra-id>shows it as deleted, with its last properties and citations. - Run
penumbra graph search "<name>" --type <Type>. It must return the kept entity and not the extra one. - Program:
entity.related(...)on the kept entity, for each relationship name, lists each moved relationship. - If a check fails, do not submit again. Use the table below.
If it goes wrong
Worth offering
- If the Shape has no property for other names, offer to add one, so the extra name is kept (recipe change-a-shape).
- Offer to look for other entities of the same type that may be one thing (problem same-name-two-entities).
Report
Tell the person:- Which entity you kept and which you deleted, with both ids.
- The relationships that moved, by type and other end, and any that you left out and why.
- The properties and citations you carried over.
- That the deleted entity can be restored by id, and what a restore does not bring back.
- Whether the submit is applied or waits for approval, with the approval URL.
- The request id of the submit.