feat: upgrade panproto to v0.22.0 — field transforms on formatted strings panproto v0.22.0 fixes ComputeField transforms on string fields with format annotations (at-uri, datetime). Previously these were stored as child vertices and invisible to field transforms; now they're accessible as extra_fields. This makes at_uri_extract_did/at_uri_extract_name work correctly for Tangled issue/pull repo URIs, producing repoDid and repoName via panproto's expression language — no manual decomposition needed. Also parses from body vertex (:body) instead of record vertex, so field transforms are applied at the correct schema level. Added test: tangled_issue_transform_decomposes_repo_uri verifies the full parse → lift → to_json pipeline produces repoDid/repoName.
Author: Aaron Steven White
Commit
c6bfa57d1672df298ea0401efbc3405ec59db0baParent: bbdd3bdf41
Structural diff unavailable
These commits were pushed via plain git push, so no pre-parsed
schemas are available. Install git-remote-cospan and re-push via panproto:// to
see scope-level changes, breaking change detection, and semantic diffs.
brew install panproto/tap/git-remote-cospan4 files changed +116 -70
@@ -436,7 +436,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
436436 437437 [[package]] 438438 name = "cospan-appview" 439-version = "0.6.0" 439+version = "0.6.3" 440440 dependencies = [ 441441 "anyhow", 442442 "async-trait",
@@ -482,7 +482,7 @@ dependencies = [
482482 483483 [[package]] 484484 name = "cospan-codegen" 485-version = "0.6.0" 485+version = "0.6.3" 486486 dependencies = [ 487487 "anyhow", 488488 "panproto-check",
@@ -499,7 +499,7 @@ dependencies = [
499499 500500 [[package]] 501501 name = "cospan-node" 502-version = "0.6.0" 502+version = "0.6.3" 503503 dependencies = [ 504504 "anyhow", 505505 "async-trait",
@@ -1310,7 +1310,7 @@ dependencies = [
13101310 "libc", 13111311 "percent-encoding", 13121312 "pin-project-lite", 1313- "socket2 0.6.3", 1313+ "socket2 0.5.10", 13141314 "system-configuration", 13151315 "tokio", 13161316 "tower-service",
@@ -2001,8 +2001,8 @@ dependencies = [
20012001 20022002 [[package]] 20032003 name = "panproto-check" 2004-version = "0.20.0" 2005-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2004+version = "0.22.0" 2005+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20062006 dependencies = [ 20072007 "panproto-gat", 20082008 "panproto-lens",
@@ -2016,8 +2016,8 @@ dependencies = [
20162016 20172017 [[package]] 20182018 name = "panproto-core" 2019-version = "0.20.0" 2020-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2019+version = "0.22.0" 2020+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20212021 dependencies = [ 20222022 "panproto-check", 20232023 "panproto-gat",
@@ -2032,8 +2032,8 @@ dependencies = [
20322032 20332033 [[package]] 20342034 name = "panproto-expr" 2035-version = "0.20.0" 2036-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2035+version = "0.22.0" 2036+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20372037 dependencies = [ 20382038 "rustc-hash", 20392039 "serde",
@@ -2042,8 +2042,8 @@ dependencies = [
20422042 20432043 [[package]] 20442044 name = "panproto-expr-parser" 2045-version = "0.20.0" 2046-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2045+version = "0.22.0" 2046+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20472047 dependencies = [ 20482048 "chumsky", 20492049 "logos",
@@ -2052,8 +2052,8 @@ dependencies = [
20522052 20532053 [[package]] 20542054 name = "panproto-gat" 2055-version = "0.20.0" 2056-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2055+version = "0.22.0" 2056+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20572057 dependencies = [ 20582058 "panproto-expr", 20592059 "rustc-hash",
@@ -2063,8 +2063,8 @@ dependencies = [
20632063 20642064 [[package]] 20652065 name = "panproto-git" 2066-version = "0.20.0" 2067-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2066+version = "0.22.0" 2067+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20682068 dependencies = [ 20692069 "git2", 20702070 "miette",
@@ -2081,8 +2081,8 @@ dependencies = [
20812081 20822082 [[package]] 20832083 name = "panproto-grammars" 2084-version = "0.20.0" 2085-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2084+version = "0.22.0" 2085+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20862086 dependencies = [ 20872087 "cc", 20882088 "serde",
@@ -2093,8 +2093,8 @@ dependencies = [
20932093 20942094 [[package]] 20952095 name = "panproto-inst" 2096-version = "0.20.0" 2097-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2096+version = "0.22.0" 2097+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 20982098 dependencies = [ 20992099 "bumpalo", 21002100 "panproto-expr",
@@ -2109,8 +2109,8 @@ dependencies = [
21092109 21102110 [[package]] 21112111 name = "panproto-io" 2112-version = "0.20.0" 2113-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2112+version = "0.22.0" 2113+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 21142114 dependencies = [ 21152115 "bumpalo", 21162116 "memchr",
@@ -2132,8 +2132,8 @@ dependencies = [
21322132 21332133 [[package]] 21342134 name = "panproto-lens" 2135-version = "0.20.0" 2136-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2135+version = "0.22.0" 2136+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 21372137 dependencies = [ 21382138 "panproto-expr", 21392139 "panproto-gat",
@@ -2149,8 +2149,8 @@ dependencies = [
21492149 21502150 [[package]] 21512151 name = "panproto-mig" 2152-version = "0.20.0" 2153-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2152+version = "0.22.0" 2153+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 21542154 dependencies = [ 21552155 "panproto-expr", 21562156 "panproto-gat",
@@ -2163,8 +2163,8 @@ dependencies = [
21632163 21642164 [[package]] 21652165 name = "panproto-parse" 2166-version = "0.20.0" 2167-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2166+version = "0.22.0" 2167+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 21682168 dependencies = [ 21692169 "memchr", 21702170 "miette",
@@ -2182,8 +2182,8 @@ dependencies = [
21822182 21832183 [[package]] 21842184 name = "panproto-project" 2185-version = "0.20.0" 2186-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2185+version = "0.22.0" 2186+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 21872187 dependencies = [ 21882188 "blake3", 21892189 "globset",
@@ -2202,8 +2202,8 @@ dependencies = [
22022202 22032203 [[package]] 22042204 name = "panproto-protocols" 2205-version = "0.20.0" 2206-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2205+version = "0.22.0" 2206+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 22072207 dependencies = [ 22082208 "blake3", 22092209 "panproto-gat",
@@ -2217,8 +2217,8 @@ dependencies = [
22172217 22182218 [[package]] 22192219 name = "panproto-schema" 2220-version = "0.20.0" 2221-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2220+version = "0.22.0" 2221+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 22222222 dependencies = [ 22232223 "panproto-expr", 22242224 "panproto-gat",
@@ -2230,8 +2230,8 @@ dependencies = [
22302230 22312231 [[package]] 22322232 name = "panproto-vcs" 2233-version = "0.20.0" 2234-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2233+version = "0.22.0" 2234+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 22352235 dependencies = [ 22362236 "blake3", 22372237 "panproto-check",
@@ -2251,8 +2251,8 @@ dependencies = [
22512251 22522252 [[package]] 22532253 name = "panproto-xrpc" 2254-version = "0.20.0" 2255-source = "git+https://github.com/panproto/panproto.git?tag=v0.20.0#2ded0cd8b8027b86432d5b20e7860664b81a6430" 2254+version = "0.22.0" 2255+source = "git+https://github.com/panproto/panproto.git?tag=v0.22.0#9614c9e266727261af4b5a36896241469a30b15a" 22562256 dependencies = [ 22572257 "hex", 22582258 "miette",
@@ -2500,7 +2500,7 @@ dependencies = [
25002500 "quinn-udp", 25012501 "rustc-hash", 25022502 "rustls", 2503- "socket2 0.6.3", 2503+ "socket2 0.5.10", 25042504 "thiserror", 25052505 "tokio", 25062506 "tracing",
@@ -2537,7 +2537,7 @@ dependencies = [
25372537 "cfg_aliases", 25382538 "libc", 25392539 "once_cell", 2540- "socket2 0.6.3", 2540+ "socket2 0.5.10", 25412541 "tracing", 25422542 "windows-sys 0.60.2", 25432543 ]
@@ -16,22 +16,22 @@ repository = "https://github.com/cospan-dev/cospan"
1616 # panproto — all from git because panproto-grammars needs vendored C sources 1717 # (too large for crates.io). All crates must come from the same source to avoid 1818 # type mismatches between git and crates.io versions of panproto-schema etc. 19-panproto-core = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 20-panproto-vcs = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 21-panproto-schema = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 22-panproto-check = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 23-panproto-lens = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 24-panproto-protocols = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 25-panproto-io = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 26-panproto-inst = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 27-panproto-gat = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 28-panproto-mig = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 29-panproto-expr = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 30-panproto-expr-parser = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 31-panproto-xrpc = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 32-panproto-parse = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0", features = ["group-all"] } 33-panproto-git = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 34-panproto-project = { git = "https://github.com/panproto/panproto.git", tag = "v0.20.0" } 19+panproto-core = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 20+panproto-vcs = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 21+panproto-schema = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 22+panproto-check = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 23+panproto-lens = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 24+panproto-protocols = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 25+panproto-io = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 26+panproto-inst = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 27+panproto-gat = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 28+panproto-mig = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 29+panproto-expr = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 30+panproto-expr-parser = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 31+panproto-xrpc = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 32+panproto-parse = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0", features = ["group-all"] } 33+panproto-git = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 34+panproto-project = { git = "https://github.com/panproto/panproto.git", tag = "v0.22.0" } 3535 git2 = "0.20" 3636 3737 # Web framework
@@ -147,18 +147,7 @@ impl RecordTransformer {
147147 record: &serde_json::Value, 148148 ) -> Option<Result<serde_json::Value>> { 149149 let morphism = self.tangled_morphisms.get(tangled_nsid)?; 150- let result = apply_morphism(morphism, record); 151- if tangled_nsid == "sh.tangled.repo.issue" { 152- if let Ok(ref json) = result { 153- tracing::info!( 154- has_repo_did = json.get("repoDid").is_some(), 155- has_repo = json.get("repo").is_some(), 156- keys = ?json.as_object().map(|o| o.keys().collect::<Vec<_>>()), 157- "ISSUE transform output" 158- ); 159- } 160- } 161- Some(result) 150+ Some(apply_morphism(morphism, record)) 162151 } 163152 } 164153
@@ -169,7 +158,9 @@ fn apply_projection(
169158 nsid: &str, 170159 record: &serde_json::Value, 171160 ) -> Result<serde_json::Value> { 172- let instance = panproto_inst::parse::parse_json(schema, nsid, record) 161+ // Jetstream delivers the record body, so parse from the body vertex. 162+ let body_vertex = format!("{nsid}:body"); 163+ let instance = panproto_inst::parse::parse_json(schema, &body_vertex, record) 173164 .map_err(|e| anyhow::anyhow!("parse {nsid}: {e:?}"))?; 174165 175166 let lifted = lift_wtype_sigma(compiled, schema, &instance)
@@ -183,8 +174,11 @@ fn apply_morphism(
183174 mapping: &CompiledInterop, 184175 record: &serde_json::Value, 185176 ) -> Result<serde_json::Value> { 177+ // Jetstream delivers the record body (not the full ATProto record wrapper), 178+ // so parse from the body vertex, not the record vertex. 179+ let body_vertex = format!("{}:body", mapping.tangled_nsid); 186180 let instance = 187- panproto_inst::parse::parse_json(&mapping.tangled_schema, &mapping.tangled_nsid, record) 181+ panproto_inst::parse::parse_json(&mapping.tangled_schema, &body_vertex, record) 188182 .map_err(|e| anyhow::anyhow!("parse {}: {e:?}", mapping.tangled_nsid))?; 189183 190184 let lifted =
@@ -104,3 +104,55 @@ fn all_lexicon_files_found() {
104104 files.len() 105105 ); 106106 } 107+ 108+#[test] 109+fn tangled_issue_transform_decomposes_repo_uri() { 110+ let root = workspace_root(); 111+ let lexicons_dir = root.join("packages/lexicons"); 112+ let morphisms = cospan_codegen::tangled_interop::compile_all_morphisms(&lexicons_dir).unwrap(); 113+ 114+ let issue_morphism = morphisms 115+ .iter() 116+ .find(|m| m.tangled_nsid == "sh.tangled.repo.issue") 117+ .expect("should have sh.tangled.repo.issue morphism"); 118+ 119+ // Simulate a Tangled issue record 120+ let record = serde_json::json!({ 121+ "$type": "sh.tangled.repo.issue", 122+ "repo": "at://did:plc:abc123/sh.tangled.repo/myrepo", 123+ "title": "Test issue", 124+ "body": "Test body", 125+ "createdAt": "2026-01-01T00:00:00Z" 126+ }); 127+ 128+ // Parse against the Tangled schema (body vertex, not record vertex) 129+ let body_vertex = format!("{}:body", issue_morphism.tangled_nsid); 130+ let instance = panproto_inst::parse::parse_json( 131+ &issue_morphism.tangled_schema, 132+ &body_vertex, 133+ &record, 134+ ) 135+ .expect("should parse Tangled issue"); 136+ 137+ // Apply morphism (includes field transforms) 138+ let lifted = panproto_mig::lift_wtype_sigma( 139+ &issue_morphism.compiled, 140+ &issue_morphism.cospan_schema, 141+ &instance, 142+ ) 143+ .expect("should lift"); 144+ 145+ // Emit JSON 146+ let output = panproto_inst::parse::to_json(&issue_morphism.cospan_schema, &lifted); 147+ 148+ eprintln!("Transform output: {}", serde_json::to_string_pretty(&output).unwrap()); 149+ 150+ // The repo AT-URI should be decomposed into repoDid + repoName 151+ assert!( 152+ output.get("repoDid").is_some(), 153+ "should have repoDid field, got keys: {:?}", 154+ output.as_object().map(|o| o.keys().collect::<Vec<_>>()) 155+ ); 156+ assert_eq!(output["repoDid"], "did:plc:abc123"); 157+ assert_eq!(output["repoName"], "myrepo"); 158+}