toolpath 0.1.3

Types, builders, and query operations for Toolpath provenance documents
Documentation
  • Coverage
  • 37.11%
    36 out of 97 items documented7 out of 7 items with examples
  • Size
  • Source code size: 46.75 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 8.83 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 28s Average build duration of successful builds.
  • all releases: 32s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • empathic/toolpath
    2 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • akesling

toolpath

Core types, builders, and query operations for Toolpath provenance documents.

Toolpath records who changed what, why, what they tried that didn't work, and how to verify all of it. Think "git blame, but for everything that happens to code — including the stuff git doesn't see."

Three objects model this: a Step is one atomic change by one actor. A Path is a DAG of steps (like a PR) — abandoned branches become implicit dead ends. A Graph collects related paths (like a release).

Overview

This crate provides the type system and query API for Toolpath. It contains:

  • Types: Document, Graph, Path, Step, ArtifactChange, and all supporting structures
  • Builders: Convenient constructors and builder methods for constructing documents
  • Serde: Full serialization/deserialization with #[serde(untagged)] document discrimination
  • Query: Graph traversal and filtering operations on step DAGs

This is the gravity well of the workspace. All other crates depend on toolpath; it depends on nothing except serde and serde_json.

Types

Document (enum: Graph | Path | Step)

Graph
  graph: GraphIdentity { id }
  paths: Vec<PathOrRef>         -- inline Path or $ref
  meta?: GraphMeta

Path
  path: PathIdentity { id, base?, head }
  steps: Vec<Step>
  meta?: PathMeta

Step
  step: StepIdentity { id, parents, actor, timestamp }
  change: HashMap<String, ArtifactChange>
  meta?: StepMeta

Building documents

use toolpath::v1::{Step, Path, Base, ArtifactChange};

// Build a step
let step = Step::new("step-001", "human:alex", "2026-01-29T10:00:00Z")
    .with_parent("step-000")
    .with_raw_change("src/main.rs", "@@ -1,1 +1,1 @@\n-hello\n+world")
    .with_intent("Fix greeting")
    .with_vcs_source("git", "abc123def456");

// Build a path
let path = Path::new(
    "path-pr-42",
    Some(Base::vcs("github:org/repo", "abc123")),
    "step-001",
);

// Branch from another path's step
let base = Base::toolpath("path-main", "step-005");

Query operations

The query module provides graph traversal and filtering over step slices:

use toolpath::v1::{Step, query};

let s1 = Step::new("s1", "human:alex", "2026-01-29T10:00:00Z")
    .with_raw_change("src/main.rs", "@@");
let s2 = Step::new("s2", "agent:claude", "2026-01-29T10:01:00Z")
    .with_parent("s1")
    .with_raw_change("src/main.rs", "@@");
let steps = vec![s1, s2];

let ancestors = query::ancestors(&steps, "s2");
let dead_ends = query::dead_ends(&steps, "s2");
let human_steps = query::filter_by_actor(&steps, "human:");
let main_rs = query::filter_by_artifact(&steps, "src/main.rs");
let all_files = query::all_artifacts(&steps);
let all_actors = query::all_actors(&steps);
let index = query::step_index(&steps);

Serialization

Documents roundtrip through JSON:

use toolpath::v1::Document;

let json_str = r#"{"Step":{"step":{"id":"s1","actor":"human:alex","timestamp":"2026-01-29T10:00:00Z"},"change":{}}}"#;
let doc = Document::from_json(json_str).unwrap();
let json = doc.to_json_pretty().unwrap();
assert!(json.contains("s1"));

The Document enum uses #[serde(untagged)] and discriminates by structure: it tries Graph (has graph + paths), then Path (has path + steps), then Step (has step + change).

Part of Toolpath

This crate is the core of the Toolpath workspace. See also: