1. Write through deltas, always
Every write stages through a delta.pb.capture and
pb.extract open, stage, and apply one for you; pass apply: false to stage
without committing, plan() to preview the diff, apply() to commit, and
revert() to undo. Do not look for a raw write path — there isn’t one.
2. Discover before you write
The shape decides what can land. Read it first, then write inside it:pb.ontology({ format: "markdown" })— the active model as a document.pb.shapes.list()— your project’s shapes. Starter shapes are not in this list: discover the curated kit withpb.shapes.starters().- From an agent:
penumbra_introspectbefore the first capture.
strict adherence it is rejected).
3. Address relationship endpoints explicitly
Staged relationships take typed endpoint references, not display names:from_entity_index / to_entity_index for entities staged in the same delta
(by stage order), or from_node_id / to_node_id for nodes already committed.
4. There are no confidence scores
Penumbra never returns a confidence number, and idiomatic code never invents one. Fitness questions get a disposition plus findings frompb.dq; evidence questions get per-claim
supported / inferred / unsupported verdicts from pb.dq.trace. Branch on
verdict.safeToAct and trace.grounded, not on thresholds.
5. Verify before consequential actions
Before an agent acts on graph context — sends the email, files the report, makes the recommendation — gate it:6. Pick the right memory verb
pb.memory.remember— one explicit memory, cheap, use freely.pb.memory.observe— digests a whole transcript into many memories. It runs a full extraction (model spend); use it from code at session boundaries, not per message.- Both land on the same memory plane, so
recall— from the SDK or the Memory MCP — reads everything.
7. Materialize deliberately
shape_workbench_materialize is dry-run by default and requires a
review_note. Compile, diff, and preview the operating surface first; then
apply with apply: true, dry_run: false. Forking a shape creates a separate
derived shape; editing changes it in place — choose deliberately.
8. Scope keys to projects
A key scoped to one project makesprojectId implicit everywhere. A key that
spans projects requires projectId on calls that take it. When an agent
operates over MCP, have it confirm context first (penumbra_context_get)
rather than assuming the active project.
9. Build in loops, not calls
Idiomatic Penumbra composes the same few loops the guides run: write → read back, register → extract → review → apply, gather → trace → check → act-or-repair. If your integration doesn’t close its loop — write something it never reads, or act on context it never verified — you are using the graph as a logbook, and it can do more than that.Quick reference
| Doing this? | Use | Not |
|---|---|---|
| Creating a delta | { name: "..." } | label, purpose |
| Staging an entity | { type, properties, name? } | top-level fields outside properties |
| Extracting | source: string | { text, ... } | source: { id } |
| Finding starter shapes | pb.shapes.starters() | pb.shapes.list() |
| Neighbor expansion in search | includeNeighbors: 0 | 1 | 2 | true / false |
| Expressing uncertainty | dispositions, findings, gaps | confidence scores |