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.
  • statusrunning, paused, completed, or failed.
  • graphsteps map and order traversal list.
  • refs — named step tips such as main and fork_point.
  • transcript — model/tool transcript entries linked to step IDs.
  • manifest — run kind, model/tool details, and replay/resume capability flags.
  • policies, cache, and metadata — 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.

run_graph.py
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.

navigation.py
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.

inspect_graph.py
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 tip

Multiple Parents

Most runs are a simple chain, but the data model supports multiple parents for merged evidence or future parallel branches.

merge_step.py
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.