API Reference
LineageStore is the public entry point, available from the top-level ancestree package. Everything below is a method on it, apart from the two node types at the end.
The store is grouped here by what you are trying to do rather than alphabetically.
| Section | What it covers |
|---|---|
| Creating a store | Opening a store and setting its rules and policy |
| Recording work | Writing nodes, artifacts and metadata |
| Searching and querying | Finding nodes and walking lineage |
| Visualisation | The static snapshot and the live explorer |
| Maintenance | Pruning, compacting, backing up and exporting |
| Introspection | Direct SQL and store-level statistics |
| Working with nodes | The two node types you receive |
Creating a store
A store is a directory holding one SQLite database. Rules, generation triggers and the reuse_identical/delta policy are written in at creation and read back on every later open, so reopening by path is enough. See Caveats for what cannot be changed afterwards.
ancestree.LineageStore
Orchestrates the lineage and interactions of a data pipeline.
The entire durable store is one SQLite database (<root>/ancestree.db)
holding metadata and deduplicated artifact chunks. A store can be
recreated from its root at any time; rules and policy persist in the
database itself.
Opens (creating if needed) the store at root.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
Path | str
|
The store's directory; the database lives inside it. |
required |
rules
|
dict[str, list[str]] | None
|
Allowed transitions, e.g. |
None
|
gen_triggers
|
list[str] | None
|
Step types that start a new generation. Persisted at creation; immutable afterwards. |
None
|
reuse_identical
|
bool | None
|
A node with the same parents and content as an existing one is bound to that node instead of being written again (persisted at creation; defaults to True). |
None
|
delta
|
bool | None
|
Layer-2 storage policy — a new chunk similar to one already stored is kept as a delta against it. Layer-1 chunking and exact chunk deduplication always run, whatever this is set to (persisted at creation; defaults to True). |
None
|
Raises:
| Type | Description |
|---|---|
SchemaError
|
If |
Source code in src/ancestree/store.py
Recording work
The one context manager everything else depends on. On clean exit the node commits atomically; if the block raises, partial work is kept and flagged healthy=False; an untouched node is discarded with a warning.
create_node
Creates a new node while enforcing lineage rules.
Yields a recording handle: write artifacts with node / "file"
and attach metadata with node.add_meta. On clean exit the node
is committed atomically; if the block raises, whatever was written
is persisted with healthy=False (partial work is evidence). An
untouched node is discarded with a warning.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
step_type
|
str
|
The type of pipeline step being performed. |
required |
parent
|
ParentArg
|
A Node/handle/node_id, or a list of them for a join. Every parent must exist in this store. None creates a root. |
None
|
Raises:
| Type | Description |
|---|---|
InvalidTransition
|
If the transition is not permitted. |
ValueError
|
For an unusable step_type or unknown parent. |
Source code in src/ancestree/store.py
Searching and querying
find and latest search the whole store; lineage, ancestors, children and from_parent move around the graph. Filters match structural attributes and searchable metadata in one namespace, and a callable is treated as a predicate.
find
Nodes matching every filter, oldest first. Filters match structural attributes (step_type, generation, healthy, ...) and searchable metadata by equality; pass a callable for a predicate (it receives the stored value, or None when the key is absent).
Examples:
Source code in src/ancestree/store.py
latest
The most recently created node matching the filters, or None.
get
Resolves a node_id (or an existing Node/handle) into a Node record. Returns None for None, "none", or an unknown id.
Source code in src/ancestree/store.py
lineage
The node's full ancestry plus itself, oldest first; every node appears once, after all of its parents.
Raises:
| Type | Description |
|---|---|
NodeNotFound
|
If the node is not in this store. |
Source code in src/ancestree/store.py
ancestors
find restricted to the node's lineage: ancestors (plus the
node itself) matching every filter, in lineage order.
Source code in src/ancestree/store.py
children
The direct children of the node (empty for unknown ids).
Source code in src/ancestree/store.py
from_parent
Shortcut for the previous step's outputs: matching artifact paths from the node's parent(s), in parent order. Empty when the node or its parents cannot be resolved.
Source code in src/ancestree/store.py
Visualisation
Two ways to look at a store. export_graph writes a self-contained, view-only file you can share; serve_graph serves the searchable explorer, with diffs and the runs table, on localhost.
export_graph
Renders the store into one shareable HTML file: a view-only snapshot: the lineage graph plus click-to-view metadata (search lives in the live server, so the query grammar exists once).
Small images inline as data URIs; other artifacts are copied
beside the file (<name>_files/) so links work offline. Pass
include_artifacts=False for a metadata-only snapshot of a
store with huge artifacts. Defaults to
<root>/interactive_pipeline.html; returns the written path.
Source code in src/ancestree/store.py
serve_graph
Serves the searchable explorer on 127.0.0.1 and returns its URL.
By default, this method runs non-blocking (block=False) and automatically
opens the live application graph in your default web browser (open_browser=True).
Calling it again replaces the running background server, so re-running a
notebook cell restarts the explorer rather than leaking a second one.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
port
|
int
|
Port to bind to. Defaults to 0 (assigns an ephemeral, free port). |
0
|
block
|
bool
|
If True, blocks execution using a loop until a KeyboardInterrupt is received. |
False
|
open_browser
|
bool
|
If True, automatically launches the application in the system browser. |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
The local URL running the application server. |
Source code in src/ancestree/store.py
Maintenance
Deleting, reclaiming space, and getting data out. prune defaults to a dry run. backup is the safe way to copy a store that is open; see Caveats for why copying ancestree.db by hand is not.
prune
Deletes a node and the descendants it solely supports, and reclaims the space they occupied.
A descendant is removed only when EVERY one of its parents is also
being removed. A child still reachable from an unpruned branch
survives, and its edge to the pruned parent disappears via the
foreign-key cascade. Preview with the default dry_run=True,
which never deletes and never compacts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
NodeLike
|
The node to prune. |
required |
dry_run
|
bool
|
Preview only (the default). Nothing is deleted. |
True
|
compact
|
bool
|
Reclaim the freed chunk space afterwards (the
default). Pass False when pruning many nodes in a loop and
call |
True
|
Returns:
| Type | Description |
|---|---|
list[Node]
|
The nodes that were (or would be) deleted, deepest first. |
Source code in src/ancestree/store.py
compact
Reclaims space: deletes chunks no artifact references (a delta
base still in use survives, the one-hop closure of AD5), then
returns freed pages to the OS via incremental_vacuum and
truncates the WAL.
prune calls this for you, so it is only needed directly after a
batch of prune(..., compact=False) calls, or to tidy a store
pruned by an older version. It does not touch the session read
cache (<root>/.cache/), which is transient and cleaned up
automatically when the store closes.
Returns:
| Type | Description |
|---|---|
int
|
The number of chunks removed. |
Source code in src/ancestree/store.py
backup
Writes a consistent, self-contained copy of the whole store, safe to take while the store is open and being written to.
A live store is not one file: WAL journalling keeps recent commits
in ancestree.db-wal until a checkpoint, so copying
ancestree.db out from under an open store silently loses
everything since the last one. This goes through SQLite's online
backup API, which reads through the WAL and produces a fully
checkpointed single file, which is the correct way to back a store up
without closing it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dest
|
Path | str
|
A path ending in |
required |
Returns:
| Type | Description |
|---|---|
Path
|
The path of the database file written. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the destination is this store's own database. |
Source code in src/ancestree/store.py
export_metadata
Writes grep-able JSON sidecars: one meta.json per node under
<dest>/<node_id>/ (default <root>/export), holding the
node's structural facts, provenance, metadata envelopes and
artifact digests. The database remains the source of truth; this
is the file-portability escape hatch (AD9), so lineage stays
legible to grep even if ancestree is uninstalled tomorrow.
Returns:
| Type | Description |
|---|---|
Path
|
The export directory. |
Source code in src/ancestree/store.py
close
Releases the store's connections and wipes the session read cache. Idempotent; reopen by constructing a new LineageStore. Runs automatically when the store is garbage collected or the interpreter exits, so calling it explicitly is optional.
Source code in src/ancestree/store.py
Introspection
The escape hatches, for questions the API above does not cover.
sql
Runs a read-only SELECT over the documented schema (blueprint
section 6) and returns the rows. The connection is opened read-only
with PRAGMA query_only, so writes are impossible by
construction, so the escape hatch can never corrupt invariants.
Examples:
Source code in src/ancestree/store.py
stats
Store-level numbers that make deduplication visible: node/
artifact/chunk counts, the bytes the artifacts add up to versus the
bytes the chunk pool actually holds, the dedup ratio
(artifact_bytes ÷ chunk_stored_bytes; higher is better) and
the store's size on disk.
database_bytes counts the database file plus its write-ahead
log: mid-session most recently written bytes live in the WAL, so
counting only ancestree.db understates real disk usage by
orders of magnitude until the next checkpoint.
Source code in src/ancestree/store.py
Working with nodes
You never construct a node yourself. LineageStore.create_node yields a recording handle, which is the object you write artifacts and metadata through inside the with block. The store's search and lineage methods return immutable Node records for everything already persisted.
ancestree.Node
dataclass
One persisted pipeline step: an immutable, hashable record.
Structural facts are attributes; metadata holds the user's entries;
artifacts() and / return readable paths (reassembled on demand
from the store; a node is a database row, not a directory).
Source code in src/ancestree/domain/node.py
metadata
property
The node's user metadata: each key maps to its envelope
{'value', 'data_type', 'group', 'searchable'}. Structural facts
(step_type, generation, ...) are attributes, not metadata.
provenance
property
Who/what/how produced this node: user, python_version, platform, git_commit, git_dirty, git_branch.
artifacts(contains='*')
The node's artifact files as readable paths, reassembled from the store on demand (served from the session read cache).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
contains
|
str
|
A glob pattern, or a plain substring matched case-insensitively anywhere in the filename. Defaults to all files. |
'*'
|
Source code in src/ancestree/domain/node.py
__truediv__(relative)
Read-side /: a readable path for one artifact.
Raises:
| Type | Description |
|---|---|
ArtifactNotFound
|
If the node has no artifact at that path. |
Source code in src/ancestree/domain/node.py
ancestree.domain.node.RecordingNode
The mutable handle a create_node block writes through.
node / "file.csv" returns a real, ready-to-write path inside the
node's transient scratch directory; add_meta records metadata to be
persisted at block exit. After the block closes the handle stays valid
as a parent reference (it carries the node_id).
Source code in src/ancestree/domain/node.py
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | |
add_meta(key, value, group='General', data_type='auto', searchable=True)
Attaches a piece of metadata to the node.
Searchable entries can be matched by the store's query methods; everything shows in the web graph. Adding an existing key overwrites the previous entry.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
The name of the metadata entry. |
required |
value
|
Any
|
The value to store. Must be JSON-serialisable (numpy/ pandas values are coerced with a warning). |
required |
group
|
str | None
|
A heading to group related entries under in the web graph. Defaults to "General". |
'General'
|
data_type
|
str
|
How the value renders: 'auto' (infer), 'image', 'link', 'table' (DataFrame), 'json', 'code', or 'text'. |
'auto'
|
searchable
|
bool
|
False for display-only metadata. |
True
|
Source code in src/ancestree/domain/node.py
artifacts(contains='*')
The files written so far, as native scratch paths, matching the
record's artifacts() semantics so code inside the block reads
what it just wrote at native speed.
Source code in src/ancestree/domain/node.py
__truediv__(relative)
Write-side /: a ready-to-write path inside the node's
scratch directory (intermediate directories are created).
Raises:
| Type | Description |
|---|---|
ValueError
|
If the path escapes the node's directory. |