Run Graphs
The route is still called run-trees for compatibility, but the current .tine format stores a content-addressed DAG. A run keeps steps in graph.steps, stable traversal order in graph.order, and named tips in refs.
The Run Object
A Run contains graph data plus the execution context needed to inspect, verify, fork, replay, or resume when supported.
format_version— current compatibility version,1.run_id— stable identifier for the run.status—running,paused,completed, orfailed.graph—stepsmap andordertraversal list.refs— named step tips such asmainandfork_point.transcript— model/tool transcript entries linked to step IDs.manifest— run kind, model/tool details, and replay/resume capability flags.policies,cache, andmetadata— policy snapshots, cache provenance, and integrity metadata.
Adding Steps
Use run.add_step() for normal code. It resolves parent refs, computes the full SHA-256 step ID, appends the step to the graph, and advances the selected ref.
1from opentine import Run, StepKind
2
3run = Run(
4 id="research-01",
5 model_info="claude-sonnet-4-20250514",
6 system_prompt="You are a research assistant.",
7 user_prompt="Find recent papers on RLHF",
8)
9
10root = run.add_step(
11 kind=StepKind.think,
12 inputs={"text": "Search for recent RLHF papers."},
13)
14
15search = run.add_step(
16 kind=StepKind.tool,
17 inputs={"name": "search", "arguments": {"query": "RLHF papers 2024"}},
18 outputs={"results": ["paper_a", "paper_b"]},
19 parent_ids=[root.id],
20 duration=1.2,
21)
22
23summary = run.add_step(
24 kind=StepKind.done,
25 inputs={"text": "Recent RLHF advances include..."},
26 outputs={"summary": "Found 2 relevant papers."},
27 parent_ids=[search.id],
28 duration=3.4,
29 cost=0.003,
30)
31
32run.save("research.tine")
Navigating the Graph
Run.steps is a traversal view over graph.order. The stored artifact remains graph-shaped and uses parent_idsfor ancestry.
1# Stable traversal view
2steps = run.steps
3
4# Top-level steps with no parents
5roots = run.root_steps()
6
7# Direct children of a step
8children = run.children(root.id)
9
10# Ancestors are returned root-first, ending at the requested step
11chain = run.ancestors(summary.id)
12
13# IDs can be resolved by full ID or unique prefix
14same_step = run.get_step(summary.id[:12])
Stored Shape
The JSON artifact stores step objects by full ID rather than as a flat top-level array.
data = run.to_dict()
print(data["format_version"]) # 1
print(data["graph"]["steps"].keys()) # full SHA-256 step IDs
print(data["graph"]["order"]) # stable display order
print(data["refs"]["main"]) # current branch tipMultiple Parents
Most runs are a simple chain, but the data model supports multiple parents for merged evidence or future parallel branches.
merged = run.add_step(
kind=StepKind.model,
inputs={"prompt": "Compare both sources."},
outputs={"text": "Source A and source B agree on..."},
parent_ids=[source_a.id, source_b.id],
)Why Graphs?
Graph ancestry lets opentine preserve known-good work, fork from a specific step, compare two runs by common ancestor, and cache or replay the exact recorded graph prefix without flattening branch metadata away.