Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/path-cli/src/cmd_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ mod tests {
#[test]
fn newest_version_resolves_and_is_json() {
let k = kinds::resolve("agent-coding-session").unwrap();
assert_eq!(k.version, "v1.1.0");
assert_eq!(k.uri, toolpath::v1::PATH_KIND_AGENT_CODING_SESSION);
let _: serde_json::Value =
serde_json::from_str(k.schema).expect("bundled schema is valid JSON");
}
Expand Down
53 changes: 51 additions & 2 deletions crates/path-cli/src/kinds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
//! The binary ships a copy of every kind spec it knows about so that
//! `path kind` and `path query --kind` work offline. [`BUNDLED_KINDS`] is the
//! single source of truth for which `(name, version)` specs are baked in;
//! [`crate::schema`] (kind-aware validation) and the query layer both read it.
//! the `schema` module (kind-aware validation) and the query layer both read
//! it.
//!
//! A `meta.kind` value is a semver-versioned URI of the form
//! `…/kinds/<name>/v<major>.<minor>.<patch>`. A [`KindSelector`] matches a
Expand Down Expand Up @@ -196,6 +197,54 @@ mod tests {
}
}

/// Registration glue and schema internals must move together: each
/// entry's URI, its schema's `$id` and `meta.kind` const, and the
/// `kinds/<name>/<version>/` directory the schema is included from must
/// all name the same version, and the newest bundled URI must be the one
/// production stamps on derived paths.
#[test]
fn bundled_kinds_are_internally_consistent() {
for k in BUNDLED_KINDS {
let ctx = format!("{}/{}", k.name, k.version);
let schema: serde_json::Value =
serde_json::from_str(k.schema).expect("bundled schema is valid JSON");
assert_eq!(
schema["$id"],
serde_json::json!(format!("{}/schema.json", k.uri)),
"{ctx}: schema `$id` must be the entry URI plus `/schema.json`"
);
assert_eq!(
schema["properties"]["meta"]["properties"]["kind"]["const"],
serde_json::json!(k.uri),
"{ctx}: schema's `meta.kind` const must equal the entry URI"
);
let (name, ver) = parse_kind_uri(k.uri).expect("bundled URI parses");
assert_eq!(name, k.name, "{ctx}: URI name segment");
assert_eq!(
format!("v{}.{}.{}", ver.major, ver.minor, ver.patch),
k.version,
"{ctx}: URI version segment"
);
// `schema` is a compile-time include; require it byte-identical
// to the file in the directory named by `version`, so the include
// path can't point at a different version than the entry claims.
let on_disk = std::fs::read_to_string(format!(
"{}/kinds/{}/{}/schema.json",
env!("CARGO_MANIFEST_DIR"),
k.name,
k.version
))
.expect("schema file exists under kinds/<name>/<version>/");
assert_eq!(
on_disk, k.schema,
"{ctx}: bundled schema must be included from its version directory"
);
}

let newest = BUNDLED_KINDS.last().expect("at least one bundled kind");
assert_eq!(newest.uri, toolpath::v1::PATH_KIND_AGENT_CODING_SESSION);
}

#[test]
fn parse_uri_extracts_name_and_version() {
let (name, ver) =
Expand Down Expand Up @@ -256,7 +305,7 @@ mod tests {
#[test]
fn resolve_picks_newest_for_bare_name() {
let k = resolve("agent-coding-session").expect("bundled");
assert_eq!(k.version, "v1.1.0");
assert_eq!(k.uri, toolpath::v1::PATH_KIND_AGENT_CODING_SESSION);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion crates/path-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ mod fuzzy;
#[cfg(not(target_os = "emscripten"))]
pub mod harness;
mod io;
mod kinds;
pub mod kinds;
mod query;
mod schema;
#[cfg(all(not(target_os = "emscripten"), feature = "embedded-picker"))]
Expand Down
2 changes: 1 addition & 1 deletion crates/path-cli/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ mod tests {
fn wrap_graph_filters_by_kind() {
let graph = Graph::from_path(forked_path());
let mut out = Vec::new();
let sel = kinds::parse_kind_selector("agent-coding-session/v1.1.0");
let sel = kinds::parse_kind_selector(toolpath::v1::PATH_KIND_AGENT_CODING_SESSION);
wrap_graph(&doc_src("g"), &graph, Some(&sel), None, &mut out);
assert_eq!(out.len(), 4, "matching kind keeps all steps");

Expand Down
2 changes: 1 addition & 1 deletion crates/path-cli/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ mod tests {
validate(&doc).expect("base is optional on path identity");
}

const ACS_KIND: &str = "https://toolpath.net/kinds/agent-coding-session/v1.1.0";
const ACS_KIND: &str = toolpath::v1::PATH_KIND_AGENT_CODING_SESSION;

fn acs_graph(append: serde_json::Value) -> serde_json::Value {
json!({
Expand Down
20 changes: 11 additions & 9 deletions crates/path-cli/tests/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,23 +473,25 @@ fn raw_unescapes_string_content() {

#[test]
fn kind_lists_bundled_kinds() {
cmd()
.arg("kind")
.assert()
.success()
.stdout(predicate::str::contains("agent-coding-session"))
.stdout(predicate::str::contains("v1.1.0"));
let mut assert = cmd().arg("kind").assert().success();
for k in path_cli::kinds::BUNDLED_KINDS {
assert = assert
.stdout(predicate::str::contains(k.name))
.stdout(predicate::str::contains(k.version));
}
}

#[test]
fn kind_prints_newest_schema() {
let newest = path_cli::kinds::resolve("agent-coding-session").expect("bundled");
cmd()
.args(["kind", "agent-coding-session"])
.assert()
.success()
.stdout(predicate::str::contains(
"kinds/agent-coding-session/v1.1.0/schema.json",
));
.stdout(predicate::str::contains(format!(
"kinds/{}/{}/schema.json",
newest.name, newest.version
)));
}

#[test]
Expand Down
16 changes: 10 additions & 6 deletions crates/toolpath-convo/src/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -863,9 +863,7 @@ mod tests {
);
// ...and survives a JSON round-trip.
let json = serde_json::to_string(&path).unwrap();
assert!(
json.contains(r#""kind":"https://toolpath.net/kinds/agent-coding-session/v1.1.0""#)
);
assert!(json.contains(&format!(r#""kind":"{PATH_KIND_AGENT_CODING_SESSION}""#)));
}

#[test]
Expand Down Expand Up @@ -1100,9 +1098,15 @@ mod tests {
"derive_path must stamp the agent-coding-session kind"
);

let schema_src = std::fs::read_to_string(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../path-cli/kinds/agent-coding-session/v1.1.0/schema.json"
// Validate against the schema for the version the constant names, so
// this test tracks the current kind without a spelled-out version.
let version = PATH_KIND_AGENT_CODING_SESSION
.rsplit('/')
.next()
.expect("kind URI has a version segment");
let schema_src = std::fs::read_to_string(format!(
"{}/../path-cli/kinds/agent-coding-session/{version}/schema.json",
env!("CARGO_MANIFEST_DIR")
))
.expect("read kind schema");
let schema: serde_json::Value = serde_json::from_str(&schema_src).unwrap();
Expand Down
11 changes: 6 additions & 5 deletions crates/toolpath/src/jsonl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1249,24 +1249,25 @@ mod tests {
}),
};
let jsonl = p.to_jsonl_string().unwrap();
assert!(
jsonl.contains(r#""kind":"https://toolpath.net/kinds/agent-coding-session/v1.1.0""#)
);
assert!(jsonl.contains(&format!(
r#""kind":"{}""#,
crate::v1::PATH_KIND_AGENT_CODING_SESSION
)));
let back = Path::from_jsonl_str(&jsonl).unwrap();
assert_eq!(canonical_json(&p), canonical_json(&back));
}

#[test]
fn path_meta_line_can_set_kind() {
let patch = PathMetaPatch {
kind: Some("https://toolpath.net/kinds/agent-coding-session/v1.1.0".into()),
kind: Some(crate::v1::PATH_KIND_AGENT_CODING_SESSION.into()),
..Default::default()
};
let mut meta = PathMeta::default();
apply_meta_patch(&mut meta, patch);
assert_eq!(
meta.kind.as_deref(),
Some("https://toolpath.net/kinds/agent-coding-session/v1.1.0")
Some(crate::v1::PATH_KIND_AGENT_CODING_SESSION)
);
}

Expand Down
16 changes: 11 additions & 5 deletions crates/toolpath/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -835,13 +835,19 @@ mod tests {
..Default::default()
};
let json = serde_json::to_string(&meta).unwrap();
assert!(
json.contains(r#""kind":"https://toolpath.net/kinds/agent-coding-session/v1.1.0""#)
);
assert!(json.contains(&format!(r#""kind":"{PATH_KIND_AGENT_CODING_SESSION}""#)));
let parsed: PathMeta = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.kind.as_deref(), Some(PATH_KIND_AGENT_CODING_SESSION));
}

/// The one test that spells the current kind URI out literally: every
/// other test goes through the constant, so a typo in the constant would
/// otherwise self-validate.
#[test]
fn test_path_kind_constant_spells_current_uri() {
assert_eq!(
parsed.kind.as_deref(),
Some("https://toolpath.net/kinds/agent-coding-session/v1.1.0")
PATH_KIND_AGENT_CODING_SESSION,
"https://toolpath.net/kinds/agent-coding-session/v1.1.0"
);
}

Expand Down