1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
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
#![doc = include_str!("../README.md")]
mod jsonl;
mod query;
mod types;
pub mod v1 {
//! Versioned public API for Toolpath types and queries.
//!
//! Everything you need is re-exported from this module. Types are organized
//! into four groups:
//!
//! # Documents
//!
//! The top-level types you construct, serialize, and deserialize:
//!
//! - [`Document`] — an enum that can hold any of the three document kinds
//! - [`Step`] — a single atomic change
//! - [`Path`] — a sequence of steps (e.g. a PR)
//! - [`Graph`] — a collection of paths (e.g. a release)
//!
//! # Change representation
//!
//! How individual artifact changes are described:
//!
//! - [`ArtifactChange`] — wrapper holding one or both perspectives on a change
//! - [`StructuralChange`] — language-aware AST-level operation
//!
//! # Identity and structure
//!
//! Types that wire the DAG together:
//!
//! - [`StepIdentity`] — step ID, parent links, actor, timestamp
//! - [`PathIdentity`] — path ID, base context, head pointer
//! - [`GraphIdentity`] — graph ID
//! - [`Base`] — root context (repo URI + optional ref)
//! - [`PathOrRef`] — inline path or external `$ref`
//! - [`PathRef`] — external path reference URL
//!
//! # Metadata and provenance
//!
//! Optional annotations for richer context:
//!
//! - [`StepMeta`], [`PathMeta`], [`GraphMeta`] — metadata containers
//! - [`ActorDefinition`] — full actor details (name, provider, keys)
//! - [`Identity`] — external identity reference
//! - [`Key`] — cryptographic key reference
//! - [`Ref`] — link to external resource
//! - [`Signature`] — cryptographic signature
//! - [`VcsSource`] — VCS revision reference
//!
//! # Example — build a Path with two Steps
//!
//! ```
//! use toolpath::v1::*;
//!
//! let s1 = Step::new("s1", "human:alex", "2026-01-29T10:00:00Z")
//! .with_raw_change("src/main.rs", "@@ -1 +1 @@\n-old\n+new")
//! .with_intent("Initial fix");
//!
//! let s2 = Step::new("s2", "agent:claude-code", "2026-01-29T10:05:00Z")
//! .with_parent("s1")
//! .with_raw_change("src/main.rs", "@@ -1 +1 @@\n-new\n+better")
//! .with_intent("Refine fix");
//!
//! let path = Path {
//! path: PathIdentity {
//! id: "path-1".into(),
//! base: Some(Base::vcs("github:org/repo", "abc123")),
//! head: "s2".into(),
//! graph_ref: None,
//! },
//! steps: vec![s1, s2],
//! meta: None,
//! };
//!
//! let json = serde_json::to_string_pretty(&path).unwrap();
//! assert!(json.contains("path-1"));
//! assert!(json.contains("s2"));
//! ```
/// DAG traversal and query functions for step collections.
///
/// These functions operate on `&[Step]` slices, walking parent links to
/// find ancestors, detect dead ends (abandoned branches), and filter steps
/// by actor, artifact, or time range.
///
/// # Example — find dead ends in a branching path
///
/// ```
/// use toolpath::v1::{Step, query};
///
/// let s1 = Step::new("s1", "human:alex", "2026-01-29T10:00:00Z")
/// .with_raw_change("f.rs", "@@");
/// let s2 = Step::new("s2", "agent:claude", "2026-01-29T10:01:00Z")
/// .with_parent("s1")
/// .with_raw_change("f.rs", "@@");
/// let abandoned = Step::new("s2a", "agent:claude", "2026-01-29T10:01:30Z")
/// .with_parent("s1")
/// .with_raw_change("f.rs", "@@");
/// let s3 = Step::new("s3", "human:alex", "2026-01-29T10:02:00Z")
/// .with_parent("s2")
/// .with_raw_change("f.rs", "@@");
///
/// let steps = vec![s1, s2, abandoned, s3];
///
/// let dead = query::dead_ends(&steps, "s3");
/// assert_eq!(dead.len(), 1);
/// assert_eq!(dead[0].step.id, "s2a");
///
/// let ancestors = query::ancestors(&steps, "s3");
/// assert!(ancestors.contains("s1"));
/// assert!(ancestors.contains("s3"));
/// assert!(!ancestors.contains("s2a"));
/// ```
pub mod query {
pub use crate::query::{
all_actors, all_artifacts, ancestors, dead_ends, filter_by_actor, filter_by_artifact,
filter_by_time_range, step_index,
};
}
/// JSONL streaming format for `Path` documents.
///
/// See the module-level docs in [`crate::jsonl`] for the on-wire shape.
/// Read a file with [`Path::from_jsonl_reader`] /
/// [`Path::from_jsonl_str`], write with [`Path::to_jsonl_writer`] /
/// [`Path::to_jsonl_string`].
pub mod jsonl {
pub use crate::jsonl::{
ActorDefBody, HeadBody, JsonlError, JsonlLine, PathCloseBody, PathMetaBody,
PathMetaPatch, PathOpenBody, PathOpenMeta, SignatureBody, StepBody,
};
}
pub use crate::types::{
ActorDefinition, ArtifactChange, Base, Document, Graph, GraphIdentity, GraphMeta, Identity,
Key, Path, PathIdentity, PathMeta, PathOrRef, PathRef, Ref, Signature, Step, StepIdentity,
StepMeta, StructuralChange, VcsSource,
};
}