fix: Tangled dispatch correctness + cleanup dead code - Move sh.tangled.repo/pipeline/refUpdate/issue/pull from simple dispatch to special-case arms so they get node URL lookup, breaking change counting, SSE events, and algebraic check extraction - Add shared util.rs with consolidated camel_to_snake, nsid_to_pascal, find_record_body, is_field_required (was triplicated) - Remove dead auto_camel_to_snake_renames from db_projection.rs

Author: Aaron Steven White
Commit 5ec6183c440adcd91b4da5d8158d768a814bc211
Parent: 67e8a479ce
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-cospan
6 files changed +82 -44
@@ -80,7 +80,7 @@ async fn dispatch_special_upsert(
8080 ) -> anyhow::Result<()> {
8181     match collection {
8282         // ─── Repo (node URL lookup) ─────────────────────────────────
83-        "dev.cospan.repo" => {
83+        "dev.cospan.repo" | "sh.tangled.repo" => {
8484             let node_uri = rec.get("node").and_then(|v| v.as_str()).unwrap_or("");
8585             let node_did = extract_did_from_at_uri(node_uri);
8686 
@@ -104,7 +104,7 @@ async fn dispatch_special_upsert(
104104         }
105105 
106106         // ─── Ref Update (breaking change count + SSE) ───────────────
107-        "dev.cospan.vcs.refUpdate" => {
107+        "dev.cospan.vcs.refUpdate" | "sh.tangled.git.refUpdate" => {
108108             let breaking_changes = rec
109109                 .get("breakingChanges")
110110                 .and_then(|v| v.as_array())
@@ -129,7 +129,7 @@ async fn dispatch_special_upsert(
129129         }
130130 
131131         // ─── Issue (SSE event on create) ────────────────────────────
132-        "dev.cospan.repo.issue" => {
132+        "dev.cospan.repo.issue" | "sh.tangled.repo.issue" => {
133133             let mut row: db::issue::IssueRow =
134134                 serde_json::from_value(transform_record(state, collection, rec))?;
135135             row.did = did.to_string();
@@ -226,7 +226,7 @@ async fn dispatch_special_upsert(
226226         }
227227 
228228         // ─── Pull Request (SSE event on create) ─────────────────────
229-        "dev.cospan.repo.pull" => {
229+        "dev.cospan.repo.pull" | "sh.tangled.repo.pull" => {
230230             let mut row: db::pull::PullRow =
231231                 serde_json::from_value(transform_record(state, collection, rec))?;
232232             row.did = did.to_string();
@@ -336,7 +336,7 @@ async fn dispatch_special_upsert(
336336         }
337337 
338338         // ─── Pipeline (algebraicChecks extraction) ──────────────────
339-        "dev.cospan.pipeline" => {
339+        "dev.cospan.pipeline" | "sh.tangled.pipeline" => {
340340             let checks = rec.get("algebraicChecks");
341341 
342342             let mut row: db::pipeline::PipelineRow =
@@ -80,12 +80,9 @@ pub async fn dispatch_simple_upsert(
8080         "sh.tangled.feed.reaction" => simple_upsert!(reaction, ReactionRow),
8181         "sh.tangled.repo.collaborator" => simple_upsert!(collaborator, CollaboratorRow),
8282         "sh.tangled.knot.member" => simple_upsert!(org_member, OrgMemberRow),
83-        "sh.tangled.repo" => simple_upsert!(repo, RepoRow),
84-        "sh.tangled.pipeline" => simple_upsert!(pipeline, PipelineRow),
85-        "sh.tangled.git.refUpdate" => simple_upsert!(ref_update, RefUpdateRow, no_did),
86-        "sh.tangled.repo.issue" => simple_upsert!(issue, IssueRow),
87-        "sh.tangled.repo.pull" => simple_upsert!(pull, PullRow),
88-
83+        // NOTE: sh.tangled.repo, pipeline, refUpdate, issue, pull have side effects
84+        // (node URL lookup, breaking change count, SSE events) and are handled
85+        // in consumer.rs dispatch_special_upsert, not here.
8986         _ => Ok(false),
9087     }
9188 }
@@ -324,38 +324,6 @@ fn compute_array_len(source_field: &str, target_field: &str) -> FieldTransform {
324324     }
325325 }
326326 
327-/// Auto-generate camelCase → snake_case renames for all Lexicon fields.
328-/// This ensures `to_json()` output has snake_case keys matching Row struct field names.
329-pub fn auto_camel_to_snake_renames(
330-    schema: &panproto_schema::Schema,
331-    nsid: &str,
332-) -> Vec<FieldTransform> {
333-    let body_vertex = record_body_vertex(nsid);
334-    let props = panproto_protocols::emit::children_by_edge(schema, &body_vertex, "prop");
335-    let mut renames = Vec::new();
336-    for (edge, _) in &props {
337-        if let Some(name) = edge.name.as_ref() {
338-            let camel = name.as_str();
339-            let snake = camel_to_snake(camel);
340-            if camel != snake {
341-                renames.push(rename_field(camel, &snake));
342-            }
343-        }
344-    }
345-    renames
346-}
347-
348-fn camel_to_snake(s: &str) -> String {
349-    let mut r = String::with_capacity(s.len() + 4);
350-    for (i, c) in s.chars().enumerate() {
351-        if c.is_uppercase() && i > 0 {
352-            r.push('_');
353-        }
354-        r.push(c.to_lowercase().next().unwrap_or(c));
355-    }
356-    r
357-}
358-
359327 /// Get the record body vertex ID for a given NSID.
360328 /// ATProto Lexicon schemas have the body under `{nsid}.record`.
361329 fn record_body_vertex(nsid: &str) -> String {
@@ -136,12 +136,13 @@ pub fn columns_for_record(
136136         }
137137 
138138         if let Some(ovr) = type_override {
139+            let is_optional = ovr.rust_type.starts_with("Option<");
139140             cols.push(Column {
140141                 name: snake,
141142                 camel_name: field_name.into(),
142143                 rust_type: ovr.rust_type.into(),
143144                 sql_type: ovr.sql_type.into(),
144-                optional: false,
145+                optional: is_optional,
145146                 is_counter: false,
146147             });
147148         } else {
@@ -5,3 +5,4 @@ pub mod emit_xrpc;
55 pub mod morphism;
66 pub mod record_config;
77 pub mod tangled_interop;
8+pub mod util;
cospan · schematic version control on atproto built on AT Protocol