Skip to article
Daedalus Development Group
← More from this project

George Sarris (0xcircuitbreaker) · · 5 min read

Why the Fork Primitive Matters in OpenTine 0.8

A release-pinned guide to branching portable agent artifacts and repository runs without confusing recorded ancestry with deterministic live execution.

In this article

A fork is a provenance boundary

Agent failures rarely begin at the final answer. A weak search result, an ambiguous approval, a malformed tool argument, or an unexamined assumption can push the remaining execution down the wrong path. If the only retained artifact is a transcript, debugging becomes a manual attempt to reconstruct that path.

OpenTine's fork primitive keeps the recorded history up to a selected point and creates a new branch from there. The result is not merely “try the prompt again.” It gives the alternative attempt an explicit relationship to the source run, so a later comparison can answer:

  • which recorded steps or events are shared;
  • where the new path diverged;
  • which provider, model, tool, state, cost, or usage fields changed; and
  • which evaluation or promotion applies to which immutable run.

This article is pinned to the tagged OpenTine 0.8.0 release. The April version described 0.1.0's prefix-copy behavior; the current release has a much broader file and repository implementation.

There are two fork commands because there are two stores

For a portable artifact:

tine fork failed.tine --from-step 3 --save retry.tine
tine diff failed.tine retry.tine --exit-code

--from-step accepts a decimal position, a full step ID, or a unique prefix. The new file retains the selected step's ancestor closure. The source file is not rewritten.

For a v3 repository:

tine repo-fork heads/main \
  --from-event event:sha256:... \
  --ref experiments/retry \
  --prompt "Use the verified inventory source" \
  --repo .

tine repo-diff heads/main experiments/retry --repo . --exit-code

The repository branch reuses immutable ancestor objects and moves the explicitly named ref using compare-and-swap rules. There is no implicit destination ref. That refusal is useful: a tool cannot silently overwrite a mainline or promotion because a caller forgot where the branch should land.

The two identity rules are intentionally different. A v2 file fork records the fork act—source, retained slice, branch, intent, and nonce—so two attempts from the same point can remain distinct. A v3 run ID is a content hash, so branches whose recorded content is identical resolve to the same object.

Forking does not erase the cost of the past

A portable file contains a complete run graph. Forking it can copy the retained graph into another file, so the disk cost is not just a pointer. The v3 repository is the storage model that deduplicates shared immutable objects across runs.

More importantly, preserving an old model answer does not mean that answer was free when it was first produced. The fork avoids redoing selected work during cache reuse; it does not make the original tokens, external calls, or human review disappear from accounting history.

OpenTine records cost and usage separately from content identity and can report both the amount captured at execution time and a post-hoc price from a signed catalog:

tine cost retry.tine
tine price retry.tine --at 2026-08-23

An unknown rate remains unknown. It is not converted into a fabricated zero to make a branch look cheaper.

Replay determines what happens after the fork

The current CLI distinguishes recorded reuse from live re-execution:

  • --mode cache derives the branch from recorded steps;
  • --mode rerun invokes the selected model or external harness;
  • --verify makes reproduction a CI-friendly exit-code check;
  • --inspect previews the exact retained ancestor slice.

That language matters. Cache replay over fixed saved content can reproduce the recorded structure. Rerunning a hosted model, web request, shell command, or mutable database can produce new content even with the same prompt.

tine replay run.tine --verify checks the artifact procedure: temporary write, reload, second derivation, retained slice, canonical integrity, and structural comparison. With an external harness, verification executes the harness twice and compares the two artifacts. A pass is evidence for that scoped check. It is not proof that a provider alias will emit the same tokens next month.

A practical Hermes debugging path

OpenTine 0.8.0 includes an external Hermes profile. It runs an already installed and authenticated Hermes CLI through hermes chat -q, then records the bounded JSON or text events Hermes emits:

tine run --harness hermes \
  --prompt "Inspect the failed deployment and propose a reversible fix" \
  --save hermes-baseline.tine

tine show hermes-baseline.tine
tine fork hermes-baseline.tine --from-step 4 --save hermes-review.tine
tine diff hermes-baseline.tine hermes-review.tine

This is capture around Hermes, not a claim that OpenTine replaces the Hermes runtime. Hermes owns its tools, session behavior, permissions, and authentication. OpenTine owns the run artifact, recorded parentage, verification, comparison, and optional repository workflow around that execution.

The process harness enforces output, event, line-size, and time bounds. Those controls protect the recorder from unbounded output; they are not an operating-system sandbox. A Hermes tool still needs its own least-privilege environment and approval rules.

Forks are most useful when the intervention is named

A productive fork records why the branch exists. Examples include:

  • replace an untrusted source with a primary source;
  • change one tool policy while keeping the same earlier analysis;
  • ask a second model to continue from a shared causal prefix;
  • resume a paused workflow after a human approval;
  • mutate graph state at a known node and compare the subsequent path; or
  • take the best loop candidate and explore two refinement strategies.

“Retry” is usually too vague. A branch name or recorded intent such as experiments/verified-source makes the comparison legible to someone who was not present for the original failure.

Fork, evaluate, promote

The repository flow separates experimentation from release:

tine evaluate experiments/retry \
  --evaluator regression-suite \
  --score quality=0.94 \
  --repo .

tine attest experiments/retry \
  --signer release-manager \
  --claim '{"kind":"approval","scope":"staging"}' \
  --repo .

tine promote experiments/retry --name staging --repo .

Evaluations and attestations are immutable objects about a run. The textual signer or evaluator label is self-asserted unless a trusted signature is attached and independently verified. Promotion moves a ref; it does not make an unevaluated answer correct.

This is the deeper reason the fork primitive matters. It makes experimentation a first-class part of provenance:

recorded source
      |
      +-- retained causal prefix
              |
              +-- experiment A -- evaluation -- promotion
              |
              +-- experiment B -- evaluation -- rejected

The system preserves both alternatives and the evidence used to choose between them.

Limits to keep visible

Forking is not a rollback of the external world. If the source run sent an email, placed an order, wrote a database row, or changed infrastructure, creating a new artifact does not undo that side effect. A rerun can repeat it unless the tool layer supplies idempotency keys, dry-run modes, approval gates, or compensating actions.

Forking also does not supply context that was never recorded. Reproducible branches require stable code, environment, policy, model, tool, and data references. The v3 repository can carry code, environment, policy, budget, and pricing manifests, but an application must still capture them accurately.

Use the tagged concepts guide and getting-started walkthrough for exact command semantics. Use the OpenTine project page for the DDG-level overview.

Continue reading