How to persist and reload a workspace#
This guide shows you how to save a FactGraph ledger to disk as a durable workspace and reload it later under the matching schema. Use it when a graph must outlive the process that built it, for example to hand a ledger to a separate read or evaluation step.
A workspace is a directory holding the ledger (ledger.db), a schema object under db/, and a manifest (factgraph_workspace.json). Reload validates the stored schema digest against the Entity classes you supply, so the same classes that created the workspace must be available at load time. For the full surface and manifest layout, see Persistence: workspaces and attach.
Prerequisites#
- Your
Entityclasses are defined and importable from both the writing and the reading code paths. See How to define a schema.
Steps#
Bind the graph to a workspace directory when you create it, then write facts as usual.
from factgraph.sdk import Entity, FactGraph, Field, Identity class User(Entity): user_id: str = Identity() name: str = Field() fg = FactGraph.create(schema_classes=[User], path="./my_workspace") alice = fg.entities.create(User, user_id="Alice") fg.fields.set(User.name, alice, "Alice")Passing
path=toFactGraph.create(...)makes the graph own a durable workspace. Writing facts goes through the normal Layer 1 and Layer 2 namespaces. See How to write and read facts.Save the workspace to disk.
fg.save_workspace()Because the graph was created with
path=,save_workspace()needs no argument: it writes to the bound path and returns{"path": ..., "manifest": ...}. Saving is idempotent, so calling it again after more writes overwrites the same workspace in place.If you did not create the graph with
path=, pass the directory once. That call also binds the path for later no-argument saves:fg = FactGraph.create(schema_classes=[User]) # ... writes ... fg.save_workspace("./my_workspace") # writes and binds the path fg.save_workspace() # subsequent saves reuse itCalling
save_workspace()on a graph with no bound path and no argument raisesSDKStoreErrorwith the messageworkspace path not bound; pass fg.save_workspace(path=...) or create with FactGraph.create(path=...).In a fresh process or session, reload the workspace under the same
Entityclasses.from factgraph.sdk import FactGraph fg = FactGraph.load_workspace("./my_workspace", schema_classes=[User]) snap = fg.entities.get(User, user_id="Alice")schema_classesis required: a class-less load raisesSDKStoreErrorwith the messageschema_classes is required for FactGraph.load_workspace(...). The reloaded graph restores the ledger, so reads return the facts you saved.To verify:
snapis the restoredUserentity, withsnap.name == "Alice".
The schema-digest guard#
load_workspace compiles the supplied schema_classes, computes their schema digest, and validates it against the digest recorded in the workspace manifest. If the digests disagree, the load is rejected:
class Account(Entity):
account_id: str = Identity()
name: str = Field()
# Workspace was saved with [User]; loading under [Account] is rejected.
FactGraph.load_workspace("./my_workspace", schema_classes=[Account])
# raises SDKStoreError; message mentions "schema" and "digest"The guard compares schema structure, not the compile timestamp. Reloading under the same classes after they were recompiled at a different time still succeeds: the digest is stable across a new generated_at, so the manifest digest and the recompiled digest match.
If the workspace directory has no manifest, the load is rejected with an SDKStoreError mentioning factgraph_workspace.json. A directory that still carries a legacy registry/ subdirectory is also rejected, with a message pointing at python -m factgraph migrate-workspace <workspace>.
A parallel guard protects ledgers opened directly. If you reopen a ledger file (via ledger= or ledger_path=) whose stored schema_digest does not match the current schema, construction raises SDKStoreError beginning with schema mismatch: ledger file was written with schema_digest=....
Variations#
- To save the same graph to a second location, pass a new directory:
fg.save_workspace("./other")copies the ledger into the new workspace and rebinds future no-argument saves to that path.
See also: Persistence: workspaces and attach, Errors and error codes, About auditability