diff --git a/bobbin/crates/resolver/src/normalize.rs b/bobbin/crates/resolver/src/normalize.rs index e5941a23a..de0680498 100644 --- a/bobbin/crates/resolver/src/normalize.rs +++ b/bobbin/crates/resolver/src/normalize.rs @@ -70,22 +70,6 @@ impl NormalizeRepoRefs for Artifact { } } -impl NormalizeRepoRefs for bobbin_types::sh_tangled::pipeline::Pipeline { - async fn normalize(mut self, resolver: &RepoIdResolver) -> Option { - let trig = &mut self.trigger_metadata.repo; - if trig.repo_did.is_some() { - trig.repo = None; - return Some(self); - } - let raw = trig.repo.as_deref()?; - let parsed = AtUri::::new_owned(raw).ok()?; - let did = resolve_repo_uri(resolver, &parsed).await?; - trig.repo_did = Some(did); - trig.repo = None; - Some(self) - } -} - impl NormalizeRepoRefs for SearchableRecord { async fn normalize(self, _resolver: &RepoIdResolver) -> Option { Some(self) @@ -113,7 +97,6 @@ use bobbin_types::sh_tangled::feed::reaction::Reaction; use bobbin_types::sh_tangled::knot::Knot; use bobbin_types::sh_tangled::label::definition::Definition as LabelDefinition; use bobbin_types::sh_tangled::label::op::Op as LabelOp; -use bobbin_types::sh_tangled::pipeline::status::Status as PipelineStatus; use bobbin_types::sh_tangled::public_key::PublicKey; use bobbin_types::sh_tangled::repo::Repo; use bobbin_types::sh_tangled::repo::collaborator::Collaborator; @@ -135,7 +118,6 @@ identity_normalize!( Knot, LabelDefinition, LabelOp, - PipelineStatus, PublicKey, Repo, Collaborator, diff --git a/bobbin/crates/types/src/edges.rs b/bobbin/crates/types/src/edges.rs index 62e4c522f..4e99c8750 100644 --- a/bobbin/crates/types/src/edges.rs +++ b/bobbin/crates/types/src/edges.rs @@ -18,8 +18,6 @@ use crate::sh_tangled::feed::reaction::Reaction; use crate::sh_tangled::knot::Knot; use crate::sh_tangled::label::definition::Definition as LabelDefinitionRecord; use crate::sh_tangled::label::op::Op as LabelOpRecord; -use crate::sh_tangled::pipeline::Pipeline; -use crate::sh_tangled::pipeline::status::Status as PipelineStatusRecord; use crate::sh_tangled::public_key::PublicKey; use crate::sh_tangled::repo::Repo as RepoRecord; use crate::sh_tangled::repo::artifact::Artifact; @@ -64,8 +62,6 @@ pub enum Record { Knot(Knot), LabelDefinition(LabelDefinitionRecord), LabelOp(LabelOpRecord), - Pipeline(Pipeline), - PipelineStatus(PipelineStatusRecord), PublicKey(PublicKey), Repo(RepoRecord), Artifact(Artifact), @@ -108,8 +104,6 @@ impl Record { "sh.tangled.knot" => parse!(Knot), "sh.tangled.label.definition" => parse!(LabelDefinition), "sh.tangled.label.op" => parse!(LabelOp), - "sh.tangled.pipeline" => parse!(Pipeline), - "sh.tangled.pipeline.status" => parse!(PipelineStatus), "sh.tangled.publicKey" => parse!(PublicKey), "sh.tangled.repo" => parse!(Repo), "sh.tangled.repo.artifact" => parse!(Artifact), @@ -137,8 +131,6 @@ impl Record { Self::Knot(_) => "sh.tangled.knot", Self::LabelDefinition(_) => "sh.tangled.label.definition", Self::LabelOp(_) => "sh.tangled.label.op", - Self::Pipeline(_) => "sh.tangled.pipeline", - Self::PipelineStatus(_) => "sh.tangled.pipeline.status", Self::PublicKey(_) => "sh.tangled.publicKey", Self::Repo(_) => "sh.tangled.repo", Self::Artifact(_) => "sh.tangled.repo.artifact", @@ -191,8 +183,6 @@ impl Record { Self::Knot(r) => Some(&r.created_at), Self::LabelDefinition(r) => Some(&r.created_at), Self::LabelOp(r) => Some(&r.performed_at), - Self::Pipeline(_) => None, - Self::PipelineStatus(r) => Some(&r.created_at), Self::PublicKey(r) => Some(&r.created_at), Self::Repo(r) => Some(&r.created_at), Self::Artifact(r) => Some(&r.created_at), @@ -215,7 +205,6 @@ impl Record { Self::Reaction(r) => reaction_edges(source, r), Self::Follow(r) => follow_edges(source, r), Self::LabelOp(r) => label_op_edges(source, r), - Self::PipelineStatus(r) => pipeline_status_edges(source, r), Self::Artifact(r) => artifact_edges(source, r), Self::Collaborator(r) => collaborator_edges(source, r), Self::Issue(r) => issue_edges(source, r), @@ -223,7 +212,6 @@ impl Record { Self::Pull(r) => pull_edges(source, r), Self::PullStatus(r) => pull_status_edges(source, r), Self::SpindleMember(r) => spindle_member_edges(source, r), - Self::Pipeline(r) => pipeline_edges(source, r), Self::Knot(_) => Ok(owner_self_edges("sh.tangled.knot", source)), Self::LabelDefinition(_) => Ok(owner_self_edges("sh.tangled.label.definition", source)), Self::PublicKey(_) => Ok(owner_self_edges("sh.tangled.publicKey", source)), @@ -312,11 +300,6 @@ const MIRROR_KINDS: &[(&str, &str)] = &[ ("org.tangled.graph.follow", "org.tangled.graph.follow.by"), ("org.tangled.graph.vouch", "org.tangled.graph.vouch.by"), ("sh.tangled.label.op", "sh.tangled.label.op.by"), - ("sh.tangled.pipeline", "sh.tangled.pipeline.by"), - ( - "sh.tangled.pipeline.status", - "sh.tangled.pipeline.status.by", - ), ("sh.tangled.repo.artifact", "sh.tangled.repo.artifact.by"), ( "sh.tangled.repo.collaborator", @@ -453,16 +436,6 @@ fn label_op_edges( Ok(one_edge("sh.tangled.label.op", subject, source)) } -fn pipeline_status_edges( - source: &AtUri, - record: &PipelineStatusRecord, -) -> Result, ExtractError> { - let Some(subject) = uri_subject_for_record(&record.pipeline) else { - return Ok(Vec::new()); - }; - Ok(one_edge("sh.tangled.pipeline.status", subject, source)) -} - fn artifact_edges( source: &AtUri, record: &Artifact, @@ -569,23 +542,6 @@ fn spindle_member_edges( )) } -fn pipeline_edges( - source: &AtUri, - record: &Pipeline, -) -> Result, ExtractError> { - let trigger_repo = &record.trigger_metadata.repo; - let repo_did = trigger_repo - .repo_did - .as_ref() - .unwrap_or(&trigger_repo.did) - .clone(); - Ok(one_edge( - "sh.tangled.pipeline", - SubjectRef::Did(repo_did), - source, - )) -} - fn owner_self_edges(kind: &'static str, source: &AtUri) -> Vec { crate::ids::owner_did_from_aturi(source) .map(|did| one_edge(kind, SubjectRef::Did(did), source)) @@ -1085,72 +1041,6 @@ mod tests { assert_eq!(edges[0].subject, uri_subj(pull_uri)); } - #[test] - fn pipeline_keys_on_repo_did_when_present() { - let edges = extract( - "sh.tangled.pipeline", - "at://did:plc:lyna/sh.tangled.pipeline/pl1", - json!({ - "$type": "sh.tangled.pipeline", - "workflows": [], - "triggerMetadata": { - "kind": "manual", - "repo": { - "did": "did:plc:nel", - "repoDid": "did:plc:abalone", - "knot": "oyster.cafe", - "defaultBranch": "main" - } - } - }), - ); - assert_eq!(edges.len(), 1); - assert_eq!(edges[0].kind, nsid("sh.tangled.pipeline")); - assert_eq!(edges[0].subject, did_subj("did:plc:abalone")); - } - - #[test] - fn pipeline_falls_back_to_owner_did_when_repo_did_absent() { - let edges = extract( - "sh.tangled.pipeline", - "at://did:plc:lyna/sh.tangled.pipeline/pl1", - json!({ - "$type": "sh.tangled.pipeline", - "workflows": [], - "triggerMetadata": { - "kind": "manual", - "repo": { - "did": "did:plc:nel", - "knot": "oyster.cafe", - "defaultBranch": "main" - } - } - }), - ); - assert_eq!(edges.len(), 1); - assert_eq!(edges[0].kind, nsid("sh.tangled.pipeline")); - assert_eq!(edges[0].subject, did_subj("did:plc:nel")); - } - - #[test] - fn pipeline_status_keys_on_pipeline_at_uri() { - let pipeline_uri = "at://did:plc:lyna/sh.tangled.pipeline/pl1"; - let edges = extract( - "sh.tangled.pipeline.status", - "at://did:plc:bailey/sh.tangled.pipeline.status/abcabcabcabcz", - json!({ - "$type": "sh.tangled.pipeline.status", - "createdAt": "2026-05-01T00:00:00Z", - "pipeline": pipeline_uri, - "workflow": pipeline_uri, - "status": "success" - }), - ); - assert_eq!(edges.len(), 1); - assert_eq!(edges[0].kind, nsid("sh.tangled.pipeline.status")); - assert_eq!(edges[0].subject, uri_subj(pipeline_uri)); - } - #[test] fn spindle_member_keys_on_subject_did() { let edges = extract( diff --git a/bobbin/crates/types/src/search.rs b/bobbin/crates/types/src/search.rs index 4f7ab9f7b..3a817f51a 100644 --- a/bobbin/crates/types/src/search.rs +++ b/bobbin/crates/types/src/search.rs @@ -70,8 +70,6 @@ impl SearchableRecord { | Record::Vouch(_) | Record::Knot(_) | Record::LabelOp(_) - | Record::Pipeline(_) - | Record::PipelineStatus(_) | Record::PublicKey(_) | Record::Artifact(_) | Record::Collaborator(_) diff --git a/bobbin/crates/xrpc/src/lib.rs b/bobbin/crates/xrpc/src/lib.rs index 1a20fb54b..ec2751720 100644 --- a/bobbin/crates/xrpc/src/lib.rs +++ b/bobbin/crates/xrpc/src/lib.rs @@ -58,10 +58,6 @@ use bobbin_types::sh_tangled::label::definition::{ Definition as LabelDefinition, DefinitionRecord as LabelDefinitionRecord, }; use bobbin_types::sh_tangled::label::op::{Op as LabelOp, OpRecord as LabelOpRecord}; -use bobbin_types::sh_tangled::pipeline::status::{ - Status as PipelineStatus, StatusRecord as PipelineStatusRecord, -}; -use bobbin_types::sh_tangled::pipeline::{Pipeline, PipelineRecord}; use bobbin_types::sh_tangled::public_key::{PublicKey, PublicKeyGetRecordOutput, PublicKeyRecord}; use bobbin_types::sh_tangled::repo::artifact::{Artifact, ArtifactRecord}; use bobbin_types::sh_tangled::repo::collaborator::{Collaborator, CollaboratorRecord}; @@ -535,22 +531,6 @@ pub fn router(state: AppState) -> Router { ) .route("/xrpc/sh.tangled.label.listOpsBy", get(list_label_ops_by)) .route("/xrpc/sh.tangled.label.countOpsBy", get(count_label_ops_by)) - .route( - "/xrpc/sh.tangled.pipeline.listPipelinesBy", - get(list_pipelines_by), - ) - .route( - "/xrpc/sh.tangled.pipeline.countPipelinesBy", - get(count_pipelines_by), - ) - .route( - "/xrpc/sh.tangled.pipeline.listStatusesBy", - get(list_pipeline_statuses_by), - ) - .route( - "/xrpc/sh.tangled.pipeline.countStatusesBy", - get(count_pipeline_statuses_by), - ) .route( "/xrpc/sh.tangled.repo.listArtifactsBy", get(list_artifacts_by), @@ -618,22 +598,6 @@ pub fn router(state: AppState) -> Router { ) .route("/xrpc/sh.tangled.label.listOps", get(list_label_ops)) .route("/xrpc/sh.tangled.label.countOps", get(count_label_ops)) - .route( - "/xrpc/sh.tangled.pipeline.listPipelines", - get(list_pipelines), - ) - .route( - "/xrpc/sh.tangled.pipeline.countPipelines", - get(count_pipelines), - ) - .route( - "/xrpc/sh.tangled.pipeline.listStatuses", - get(list_pipeline_statuses), - ) - .route( - "/xrpc/sh.tangled.pipeline.countStatuses", - get(count_pipeline_statuses), - ) .route("/xrpc/sh.tangled.repo.listArtifacts", get(list_artifacts)) .route("/xrpc/sh.tangled.repo.countArtifacts", get(count_artifacts)) .route( @@ -1385,8 +1349,6 @@ edge_kinds! { "sh.tangled.knot" => KnotRecord, SubjectShape::BareDid; "sh.tangled.label.definition" => LabelDefinitionRecord, SubjectShape::BareDid; "sh.tangled.label.op" => LabelOpRecord, SubjectShape::OneOfCollections(&["sh.tangled.repo.issue", "sh.tangled.repo.pull"]), mirror LabelOpBy; - "sh.tangled.pipeline" => PipelineRecord, SubjectShape::BareDid, mirror PipelineBy; - "sh.tangled.pipeline.status" => PipelineStatusRecord, SubjectShape::Collection("sh.tangled.pipeline"), mirror PipelineStatusBy; "sh.tangled.publicKey" => PublicKeyRecord, SubjectShape::BareDid; "sh.tangled.repo" => RepoRecord, SubjectShape::BareDid; "sh.tangled.repo.artifact" => ArtifactRecord, SubjectShape::BareDid, mirror ArtifactBy; @@ -3492,32 +3454,6 @@ async fn count_label_ops_by( count_mirror::(&state, q).map(Json) } -async fn list_pipelines_by( - State(state): State, - XrpcQuery(q): XrpcQuery>, -) -> Result { - list_mirror::, _>(&state, q).await -} -async fn count_pipelines_by( - State(state): State, - XrpcQuery(q): XrpcQuery, -) -> Result, XrpcError> { - count_mirror::(&state, q).map(Json) -} - -async fn list_pipeline_statuses_by( - State(state): State, - XrpcQuery(q): XrpcQuery>, -) -> Result { - list_mirror::, _>(&state, q).await -} -async fn count_pipeline_statuses_by( - State(state): State, - XrpcQuery(q): XrpcQuery, -) -> Result, XrpcError> { - count_mirror::(&state, q).map(Json) -} - async fn list_artifacts_by( State(state): State, XrpcQuery(q): XrpcQuery>, @@ -3677,34 +3613,6 @@ async fn count_label_ops( count_for::(&state, q).map(Json) } -async fn list_pipelines( - State(state): State, - XrpcQuery(q): XrpcQuery>, -) -> Result { - list_records::, _>(&state, q).await -} - -async fn count_pipelines( - State(state): State, - XrpcQuery(q): XrpcQuery, -) -> Result, XrpcError> { - count_for::(&state, q).map(Json) -} - -async fn list_pipeline_statuses( - State(state): State, - XrpcQuery(q): XrpcQuery>, -) -> Result { - list_records::, _>(&state, q).await -} - -async fn count_pipeline_statuses( - State(state): State, - XrpcQuery(q): XrpcQuery, -) -> Result, XrpcError> { - count_for::(&state, q).map(Json) -} - async fn list_artifacts( State(state): State, XrpcQuery(q): XrpcQuery>, diff --git a/bobbin/crates/xrpc/tests/extended.rs b/bobbin/crates/xrpc/tests/extended.rs index 084134b8e..72e5de656 100644 --- a/bobbin/crates/xrpc/tests/extended.rs +++ b/bobbin/crates/xrpc/tests/extended.rs @@ -186,47 +186,6 @@ fn label_op_body(subject: &AtUri, def_uri: &AtUri, value }) } -fn pipeline_body(repo_did: &Did) -> Value { - json!({ - "$type": "sh.tangled.pipeline", - "workflows": [], - "triggerMetadata": { - "kind": "manual", - "repo": { - "did": "did:plc:teq", - "repoDid": repo_did.as_ref(), - "knot": "nel.pet", - "defaultBranch": "main" - } - } - }) -} - -fn pipeline_body_owner_only(owner_did: &Did) -> Value { - json!({ - "$type": "sh.tangled.pipeline", - "workflows": [], - "triggerMetadata": { - "kind": "manual", - "repo": { - "did": owner_did.as_ref(), - "knot": "nel.pet", - "defaultBranch": "main" - } - } - }) -} - -fn pipeline_status_body(pipeline_uri: &AtUri) -> Value { - json!({ - "$type": "sh.tangled.pipeline.status", - "createdAt": "2026-05-01T00:00:00Z", - "pipeline": pipeline_uri.as_ref(), - "workflow": pipeline_uri.as_ref(), - "status": "success" - }) -} - fn artifact_body(repo_did: &Did, name: &str) -> Value { json!({ "$type": "sh.tangled.repo.artifact", @@ -509,132 +468,6 @@ async fn list_label_ops_rejects_unrelated_collection() { assert_eq!(status, StatusCode::BAD_REQUEST); } -#[tokio::test] -async fn list_pipelines_keys_on_repo_did() { - let h = Harness::new().await; - let repo_did = did("did:plc:abalone"); - let subject = at(&format!("at://{}", repo_did.as_ref())); - let spindle_did = did("did:plc:lyna"); - let rk = rkey("pl1"); - h.add_edge( - &nsid("sh.tangled.pipeline"), - &subject, - &at(&format!( - "at://{}/sh.tangled.pipeline/{}", - spindle_did.as_ref(), - rk.as_ref() - )), - ); - h.mount( - &spindle_did, - &nsid("sh.tangled.pipeline"), - &rk, - pipeline_body(&repo_did), - ) - .await; - - let app = router(h.state.clone()); - let (status, body) = json_response( - app.oneshot(list_request( - "sh.tangled.pipeline.listPipelines", - subject.as_ref(), - &[], - )) - .await - .unwrap(), - ) - .await; - assert_eq!(status, StatusCode::OK); - let items = body["items"].as_array().unwrap(); - assert_eq!(items.len(), 1); - assert_eq!( - items[0]["value"]["triggerMetadata"]["repo"]["repoDid"], - json!(repo_did.as_ref()) - ); -} - -#[tokio::test] -async fn count_pipelines_returns_zero_when_no_edges() { - let h = Harness::new().await; - let app = router(h.state.clone()); - let (_, body) = json_response( - app.oneshot(list_request( - "sh.tangled.pipeline.countPipelines", - "at://did:plc:abalone", - &[], - )) - .await - .unwrap(), - ) - .await; - assert_eq!(body["count"], json!(0)); -} - -#[tokio::test] -async fn list_pipeline_statuses_keys_on_pipeline_uri() { - let h = Harness::new().await; - let pipeline_uri = at("at://did:plc:lyna/sh.tangled.pipeline/pl1"); - let author = did("did:plc:bailey"); - let rk = rkey("s1"); - h.add_edge( - &nsid("sh.tangled.pipeline.status"), - &pipeline_uri, - &at(&format!( - "at://{}/sh.tangled.pipeline.status/{}", - author.as_ref(), - rk.as_ref() - )), - ); - h.mount( - &author, - &nsid("sh.tangled.pipeline.status"), - &rk, - pipeline_status_body(&pipeline_uri), - ) - .await; - - let app = router(h.state.clone()); - let (status, body) = json_response( - app.oneshot(list_request( - "sh.tangled.pipeline.listStatuses", - pipeline_uri.as_ref(), - &[], - )) - .await - .unwrap(), - ) - .await; - assert_eq!(status, StatusCode::OK); - let items = body["items"].as_array().unwrap(); - assert_eq!(items.len(), 1); - assert_eq!(items[0]["value"]["pipeline"], json!(pipeline_uri.as_ref())); - assert_eq!(items[0]["value"]["status"], json!("success")); -} - -#[tokio::test] -async fn pipeline_status_endpoint_rejects_bare_did_subject() { - let h = Harness::new().await; - let app = router(h.state.clone()); - let (status, body) = json_response( - app.oneshot(list_request( - "sh.tangled.pipeline.listStatuses", - "at://did:plc:lyna", - &[], - )) - .await - .unwrap(), - ) - .await; - assert_eq!(status, StatusCode::BAD_REQUEST); - assert!( - body["message"] - .as_str() - .unwrap_or_default() - .contains("sh.tangled.pipeline/"), - "{body}" - ); -} - #[tokio::test] async fn list_artifacts_keys_on_repo_did() { let h = Harness::new().await; @@ -787,88 +620,3 @@ async fn count_strings_dedupes_per_owner() { assert_eq!(body["count"], json!(3)); assert_eq!(body["distinctAuthors"], json!(1)); } - -#[tokio::test] -async fn extractor_to_xrpc_round_trip_for_pipeline() { - let h = Harness::new().await; - let repo_did = did("did:plc:abalone"); - let spindle_did = did("did:plc:lyna"); - let rk = rkey("pl1"); - let source = at(&format!( - "at://{}/sh.tangled.pipeline/{}", - spindle_did.as_ref(), - rk.as_ref() - )); - let body = pipeline_body(&repo_did); - let parsed = - bobbin_types::edges::Record::from_json_value(&nsid("sh.tangled.pipeline"), body.clone()) - .expect("parse pipeline record"); - parsed - .extract_edges(&source) - .expect("extract") - .into_iter() - .for_each(|e| h.edges.add(e)); - h.mount(&spindle_did, &nsid("sh.tangled.pipeline"), &rk, body) - .await; - - let app = router(h.state.clone()); - let (status, json) = json_response( - app.oneshot(list_request( - "sh.tangled.pipeline.listPipelines", - &format!("at://{}", repo_did.as_ref()), - &[], - )) - .await - .unwrap(), - ) - .await; - assert_eq!( - status, - StatusCode::OK, - "extractor key must match handler subject, body was {json}" - ); - let items = json["items"].as_array().unwrap(); - assert_eq!(items.len(), 1, "expected exactly one pipeline edge"); -} - -#[tokio::test] -async fn list_pipelines_drops_records_without_resolvable_repo_did() { - let h = Harness::new().await; - let owner_did = did("did:plc:nel"); - let spindle_did = did("did:plc:lyna"); - let rk = rkey("pl1"); - let source = at(&format!( - "at://{}/sh.tangled.pipeline/{}", - spindle_did.as_ref(), - rk.as_ref() - )); - let body = pipeline_body_owner_only(&owner_did); - let parsed = - bobbin_types::edges::Record::from_json_value(&nsid("sh.tangled.pipeline"), body.clone()) - .expect("parse pipeline record"); - parsed - .extract_edges(&source) - .expect("extract") - .into_iter() - .for_each(|e| h.edges.add(e)); - h.mount(&spindle_did, &nsid("sh.tangled.pipeline"), &rk, body) - .await; - - let app = router(h.state.clone()); - let (status, json) = json_response( - app.oneshot(list_request( - "sh.tangled.pipeline.listPipelines", - &format!("at://{}", owner_did.as_ref()), - &[], - )) - .await - .unwrap(), - ) - .await; - assert_eq!(status, StatusCode::OK, "body was {json}"); - let items = json["items"].as_array().unwrap(); - assert!( - items.is_empty(), - "pipeline without resolvable repoDid must be dropped, got {json}" - ); -} diff --git a/web/src/lib/api/count.ts b/web/src/lib/api/count.ts index 3f8355df7..e90d0dfa2 100644 --- a/web/src/lib/api/count.ts +++ b/web/src/lib/api/count.ts @@ -17,10 +17,6 @@ export type CountName = | "sh.tangled.label.countDefinitions" | "sh.tangled.label.countOps" | "sh.tangled.label.countOpsBy" - | "sh.tangled.pipeline.countPipelines" - | "sh.tangled.pipeline.countPipelinesBy" - | "sh.tangled.pipeline.countStatuses" - | "sh.tangled.pipeline.countStatusesBy" | "sh.tangled.publicKey.countKeys" | "sh.tangled.repo.countArtifacts" | "sh.tangled.repo.countArtifactsBy"