use super::*; impl PostgresRegistry { pub async fn verify_operation_state( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, expected_state: &OperationStateExpectation, ) -> Result<(), RegistryError> { let mut tx = self.pool.begin().await?; let row = sqlx::query( "select status, current_draft_version, latest_published_version from operations where workspace_id = $1 and id = $2 for update", ) .bind(workspace_id.as_str()) .bind(operation_id.as_str()) .fetch_optional(&mut *tx) .await? .ok_or_else(|| RegistryError::OperationNotFound { operation_id: operation_id.as_str().to_owned(), })?; verify_expected_state(&row, operation_id.as_str(), Some(expected_state))?; tx.commit().await?; Ok(()) } pub async fn create_operation( &self, workspace_id: &WorkspaceId, snapshot: &RegistryOperation, created_by: Option<&str>, ) -> Result<(), RegistryError> { if snapshot.version != 1 { return Err(RegistryError::InvalidInitialVersion { operation_id: snapshot.id.as_str().to_owned(), version: snapshot.version, }); } if self .get_operation_summary(workspace_id, &snapshot.id) .await? .is_some() { return Err(RegistryError::OperationAlreadyExists { operation_id: snapshot.id.as_str().to_owned(), }); } let mut tx = self.pool.begin().await?; if let Err(error) = insert_operation_rows(&mut tx, workspace_id, snapshot, created_by).await { if matches_operation_name_conflict(&error) { return Err(RegistryError::OperationAlreadyExists { operation_id: snapshot.id.as_str().to_owned(), }); } return Err(error); } tx.commit().await?; Ok(()) } pub async fn create_version( &self, request: CreateVersionRequest<'_>, ) -> Result<(), RegistryError> { self.create_version_inner(request, None).await } pub async fn create_version_cas( &self, request: CreateVersionRequest<'_>, expected_state: &OperationStateExpectation, ) -> Result<(), RegistryError> { self.create_version_inner(request, Some(expected_state)) .await } async fn create_version_inner( &self, request: CreateVersionRequest<'_>, expected_state: Option<&OperationStateExpectation>, ) -> Result<(), RegistryError> { let mut tx = self.pool.begin().await?; let row = sqlx::query( "select name, protocol, current_draft_version, status, latest_published_version from operations where workspace_id = $1 and id = $2 for update", ) .bind(request.workspace_id.as_str()) .bind(request.snapshot.id.as_str()) .fetch_optional(&mut *tx) .await?; let Some(row) = row else { return Err(RegistryError::OperationNotFound { operation_id: request.snapshot.id.as_str().to_owned(), }); }; let status: OperationStatus = deserialize_enum_text(&row.try_get::("status")?, "status")?; verify_expected_state(&row, request.snapshot.id.as_str(), expected_state)?; if status == OperationStatus::Archived { return Err(RegistryError::OperationArchived { operation_id: request.snapshot.id.as_str().to_owned(), }); } if request.snapshot.status != OperationStatus::Draft { return Err(RegistryError::InvalidOperationTransition { operation_id: request.snapshot.id.as_str().to_owned(), from: format!("{:?}", request.snapshot.status).to_ascii_lowercase(), action: "create_version", }); } if row.try_get::("name")? != request.snapshot.name { return Err(RegistryError::ImmutableOperationFieldChanged { operation_id: request.snapshot.id.as_str().to_owned(), field: "name", }); } let protocol: crank_core::Protocol = deserialize_enum_text(&row.try_get::("protocol")?, "protocol")?; if protocol != request.snapshot.protocol { return Err(RegistryError::ImmutableOperationFieldChanged { operation_id: request.snapshot.id.as_str().to_owned(), field: "protocol", }); } let current = from_db_version( row.try_get::("current_draft_version")?, "current_draft_version", )?; let expected = current .checked_add(1) .filter(|value| i32::try_from(*value).is_ok()) .ok_or_else(|| RegistryError::InvalidVersionSequence { operation_id: request.snapshot.id.as_str().to_owned(), expected: current, actual: request.snapshot.version, })?; if request.snapshot.version != expected { return Err(RegistryError::OperationStaleVersion { operation_id: request.snapshot.id.as_str().to_owned(), expected, actual: request.snapshot.version, }); } insert_version_row( &mut tx, request.snapshot, request.change_note, request.created_by, ) .await?; sqlx::query( "update operations set status = $1, current_draft_version = $2, updated_at = $3::timestamptz where id = $4 and workspace_id = $5", ) .bind(serialize_enum_text(&request.snapshot.status, "status")?) .bind(to_db_version(request.snapshot.version)) .bind(request.snapshot.updated_at) .bind(request.snapshot.id.as_str()) .bind(request.workspace_id.as_str()) .execute(&mut *tx) .await?; tx.commit().await?; Ok(()) } pub async fn update_operation_draft( &self, workspace_id: &WorkspaceId, snapshot: &RegistryOperation, ) -> Result<(), RegistryError> { self.update_operation_draft_inner(workspace_id, snapshot, None) .await } pub async fn update_operation_draft_cas( &self, workspace_id: &WorkspaceId, snapshot: &RegistryOperation, expected_state: &OperationStateExpectation, ) -> Result<(), RegistryError> { self.update_operation_draft_inner(workspace_id, snapshot, Some(expected_state)) .await } async fn update_operation_draft_inner( &self, workspace_id: &WorkspaceId, snapshot: &RegistryOperation, expected_state: Option<&OperationStateExpectation>, ) -> Result<(), RegistryError> { let mut tx = self.pool.begin().await?; let locked = sqlx::query( "select current_draft_version, status, latest_published_version from operations where workspace_id = $1 and id = $2 for update", ) .bind(workspace_id.as_str()) .bind(snapshot.id.as_str()) .fetch_optional(&mut *tx) .await?; let Some(locked) = locked else { return Err(RegistryError::OperationNotFound { operation_id: snapshot.id.as_str().to_owned(), }); }; verify_expected_state(&locked, snapshot.id.as_str(), expected_state)?; let current = from_db_version( locked.try_get::("current_draft_version")?, "current_draft_version", )?; let aggregate_status: OperationStatus = deserialize_enum_text(&locked.try_get::("status")?, "status")?; if aggregate_status == OperationStatus::Archived { return Err(RegistryError::OperationArchived { operation_id: snapshot.id.as_str().to_owned(), }); } if snapshot.version != current { return Err(RegistryError::OperationStaleVersion { operation_id: snapshot.id.as_str().to_owned(), expected: current, actual: snapshot.version, }); } let mut next = snapshot.clone(); next.version = current .checked_add(1) .filter(|value| i32::try_from(*value).is_ok()) .ok_or_else(|| RegistryError::InvalidVersionSequence { operation_id: snapshot.id.as_str().to_owned(), expected: current, actual: current, })?; next.status = OperationStatus::Draft; next.published_at = None; insert_version_row(&mut tx, &next, Some("saved draft"), None).await?; sqlx::query( "update operations set display_name = $1, category = $2, security_level = $3, status = 'draft', current_draft_version = $4, updated_at = $5::timestamptz where workspace_id = $6 and id = $7", ) .bind(&next.display_name) .bind(&next.category) .bind(serialize_enum_text(&next.security_level, "security_level")?) .bind(to_db_version(next.version)) .bind(next.updated_at) .bind(workspace_id.as_str()) .bind(next.id.as_str()) .execute(&mut *tx) .await?; tx.commit().await?; Ok(()) } pub async fn archive_operation( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, archived_at: &time::OffsetDateTime, ) -> Result<(), RegistryError> { self.archive_operation_inner(workspace_id, operation_id, archived_at, None) .await } pub async fn archive_operation_cas( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, archived_at: &time::OffsetDateTime, expected_state: &OperationStateExpectation, ) -> Result<(), RegistryError> { self.archive_operation_inner( workspace_id, operation_id, archived_at, Some(expected_state), ) .await } async fn archive_operation_inner( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, archived_at: &time::OffsetDateTime, expected_state: Option<&OperationStateExpectation>, ) -> Result<(), RegistryError> { let mut tx = self.pool.begin().await?; let status = sqlx::query( "select status, current_draft_version, latest_published_version from operations where workspace_id = $1 and id = $2 for update", ) .bind(workspace_id.as_str()) .bind(operation_id.as_str()) .fetch_optional(&mut *tx) .await?; let Some(status) = status else { return Err(RegistryError::OperationNotFound { operation_id: operation_id.as_str().to_owned(), }); }; verify_expected_state(&status, operation_id.as_str(), expected_state)?; let status: OperationStatus = deserialize_enum_text(&status.try_get::("status")?, "status")?; if status == OperationStatus::Archived { tx.commit().await?; return Ok(()); } sqlx::query( "update operations set status = $1, updated_at = $2::timestamptz where workspace_id = $3 and id = $4", ) .bind(serialize_enum_text(&OperationStatus::Archived, "status")?) .bind(archived_at) .bind(workspace_id.as_str()) .bind(operation_id.as_str()) .execute(&mut *tx) .await?; tx.commit().await?; Ok(()) } pub async fn delete_operation( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, ) -> Result<(), RegistryError> { self.delete_operation_inner(workspace_id, operation_id, None) .await } pub async fn delete_operation_cas( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, expected_state: &OperationStateExpectation, ) -> Result<(), RegistryError> { self.delete_operation_inner(workspace_id, operation_id, Some(expected_state)) .await } async fn delete_operation_inner( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, expected_state: Option<&OperationStateExpectation>, ) -> Result<(), RegistryError> { let mut tx = self.pool.begin().await?; let row = sqlx::query( "select status, current_draft_version, latest_published_version from operations where workspace_id = $1 and id = $2 for update", ) .bind(workspace_id.as_str()) .bind(operation_id.as_str()) .fetch_optional(&mut *tx) .await?; let Some(row) = row else { return Err(RegistryError::OperationNotFound { operation_id: operation_id.as_str().to_owned(), }); }; verify_expected_state(&row, operation_id.as_str(), expected_state)?; let status: OperationStatus = deserialize_enum_text(&row.try_get::("status")?, "status")?; let ever_published = row .try_get::, _>("latest_published_version")? .is_some(); let durable_reference = sqlx::query_scalar::<_, bool>( "select exists ( select 1 from agent_operation_bindings where operation_id = $1 union all select 1 from approval_requests where operation_id = $1 union all select 1 from invocation_logs where operation_id = $1 union all select 1 from operation_samples where operation_id = $1 union all select 1 from descriptors where operation_id = $1 )", ) .bind(operation_id.as_str()) .fetch_one(&mut *tx) .await?; if status == OperationStatus::Archived || ever_published || durable_reference { return Err(RegistryError::OperationDeleteForbidden { operation_id: operation_id.as_str().to_owned(), }); } let deleted = sqlx::query( "delete from operations where workspace_id = $1 and id = $2", ) .bind(workspace_id.as_str()) .bind(operation_id.as_str()) .execute(&mut *tx) .await? .rows_affected(); if deleted == 0 { return Err(RegistryError::OperationNotFound { operation_id: operation_id.as_str().to_owned(), }); } tx.commit().await?; Ok(()) } pub async fn has_published_agent_bindings_for_operation( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, ) -> Result { let row = sqlx::query!( "select 1 as \"present!\" from agents a join published_agents pa on pa.agent_id = a.id join agent_operation_bindings b on b.agent_id = a.id and b.agent_version = pa.version where a.workspace_id = $1 and b.operation_id = $2 limit 1", workspace_id.as_str(), operation_id.as_str(), ) .fetch_optional(&self.pool) .await?; Ok(row.is_some()) } pub async fn list_operations( &self, workspace_id: &WorkspaceId, ) -> Result, RegistryError> { let rows = sqlx::query( "select operations.id, operations.workspace_id, operations.name, operations.display_name, operations.category, operations.protocol, operations.security_level, ov.target_json, operations.status, operations.current_draft_version, operations.latest_published_version, (operations.status = 'draft' and operations.latest_published_version is null and not exists (select 1 from agent_operation_bindings b where b.operation_id = operations.id) and not exists (select 1 from approval_requests ar where ar.operation_id = operations.id) and not exists (select 1 from invocation_logs il where il.operation_id = operations.id) and not exists (select 1 from operation_samples os where os.operation_id = operations.id) and not exists (select 1 from descriptors d where d.operation_id = operations.id)) as can_delete, operations.created_at, operations.updated_at, operations.published_at from operations join operation_versions ov on ov.operation_id = operations.id and ov.version = operations.current_draft_version where operations.workspace_id = $1 order by operations.name asc", ) .bind(workspace_id.as_str()) .fetch_all(&self.pool) .await?; rows.into_iter() .map(|row| { build_operation_summary( row.try_get("id")?, row.try_get("workspace_id")?, row.try_get("name")?, row.try_get("display_name")?, row.try_get("category")?, row.try_get("protocol")?, row.try_get("security_level")?, row.try_get("target_json")?, row.try_get("status")?, row.try_get("current_draft_version")?, row.try_get("latest_published_version")?, row.try_get("can_delete")?, row.try_get("created_at")?, row.try_get("updated_at")?, row.try_get("published_at")?, ) }) .collect() } pub async fn list_operation_usage_summaries( &self, workspace_id: &WorkspaceId, created_after: &str, ) -> Result, RegistryError> { let rows = sqlx::query( "select operation_id, count(*)::bigint as calls_today, coalesce( round((sum(case when status = 'error' then 1 else 0 end)::numeric / nullif(count(*), 0)) * 100, 2), 0 )::float8 as error_rate_pct, coalesce(round(avg(duration_ms))::bigint, 0) as avg_latency_ms from invocation_logs where workspace_id = $1 and created_at >= $2::timestamptz group by operation_id", ) .bind(workspace_id.as_str()) .bind(created_after) .fetch_all(&self.pool) .await?; rows.iter().map(map_operation_usage_summary).collect() } pub async fn list_operation_agent_refs( &self, workspace_id: &WorkspaceId, ) -> Result, RegistryError> { let rows = sqlx::query!( "select b.operation_id, a.id as agent_id, a.slug as agent_slug, a.display_name from agents a join published_agents pa on pa.agent_id = a.id join agent_operation_bindings b on b.agent_id = a.id and b.agent_version = pa.version where a.workspace_id = $1 order by a.display_name asc, b.tool_name asc", workspace_id.as_str(), ) .fetch_all(&self.pool) .await?; rows.into_iter() .map(|row| { build_operation_agent_ref( row.operation_id, row.agent_id, row.agent_slug, row.display_name, ) }) .collect() } pub async fn get_operation_summary( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, ) -> Result, RegistryError> { let row = sqlx::query( "select operations.id, operations.workspace_id, operations.name, operations.display_name, operations.category, operations.protocol, operations.security_level, ov.target_json, operations.status, operations.current_draft_version, operations.latest_published_version, (operations.status = 'draft' and operations.latest_published_version is null and not exists (select 1 from agent_operation_bindings b where b.operation_id = operations.id) and not exists (select 1 from approval_requests ar where ar.operation_id = operations.id) and not exists (select 1 from invocation_logs il where il.operation_id = operations.id) and not exists (select 1 from operation_samples os where os.operation_id = operations.id) and not exists (select 1 from descriptors d where d.operation_id = operations.id)) as can_delete, operations.created_at, operations.updated_at, operations.published_at from operations join operation_versions ov on ov.operation_id = operations.id and ov.version = operations.current_draft_version where operations.workspace_id = $1 and operations.id = $2", ) .bind(workspace_id.as_str()) .bind(operation_id.as_str()) .fetch_optional(&self.pool) .await?; row.map(|row| { build_operation_summary( row.try_get("id")?, row.try_get("workspace_id")?, row.try_get("name")?, row.try_get("display_name")?, row.try_get("category")?, row.try_get("protocol")?, row.try_get("security_level")?, row.try_get("target_json")?, row.try_get("status")?, row.try_get("current_draft_version")?, row.try_get("latest_published_version")?, row.try_get("can_delete")?, row.try_get("created_at")?, row.try_get("updated_at")?, row.try_get("published_at")?, ) }) .transpose() } pub async fn get_operation_version( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, version: u32, ) -> Result, RegistryError> { let row = sqlx::query!( "select o.id, o.workspace_id, ov.name, ov.display_name, ov.category, ov.protocol, ov.security_level, ov.created_at as \"operation_created_at!: time::OffsetDateTime\", ov.created_at as \"operation_updated_at!: time::OffsetDateTime\", ov.published_at as \"operation_published_at: time::OffsetDateTime\", ov.version, ov.status, ov.target_json, ov.input_schema_json, ov.output_schema_json, ov.input_mapping_json, ov.output_mapping_json, ov.execution_config_json, ov.tool_description_json, ov.samples_json, ov.generated_draft_json, ov.config_export_json, ov.wizard_state_json, ov.change_note, ov.created_at as \"created_at!: time::OffsetDateTime\", ov.created_by from operation_versions ov join operations o on o.id = ov.operation_id where o.workspace_id = $1 and ov.operation_id = $2 and ov.version = $3", workspace_id.as_str(), operation_id.as_str(), to_db_version(version), ) .fetch_optional(&self.pool) .await?; row.map(|row| { build_operation_version_record( row.id, row.workspace_id, row.name, row.display_name, row.category, row.protocol, row.security_level, row.operation_created_at, row.operation_updated_at, row.operation_published_at, row.version, row.status, row.target_json, row.input_schema_json, row.output_schema_json, row.input_mapping_json, row.output_mapping_json, row.execution_config_json, row.tool_description_json, row.samples_json, row.generated_draft_json, row.config_export_json, row.wizard_state_json, row.change_note, row.created_at, row.created_by, ) }) .transpose() } pub async fn list_operation_versions( &self, workspace_id: &WorkspaceId, operation_id: &OperationId, ) -> Result, RegistryError> { let rows = sqlx::query!( "select o.id, o.workspace_id, ov.name, ov.display_name, ov.category, ov.protocol, ov.security_level, ov.created_at as \"operation_created_at!: time::OffsetDateTime\", ov.created_at as \"operation_updated_at!: time::OffsetDateTime\", ov.published_at as \"operation_published_at: time::OffsetDateTime\", ov.version, ov.status, ov.target_json, ov.input_schema_json, ov.output_schema_json, ov.input_mapping_json, ov.output_mapping_json, ov.execution_config_json, ov.tool_description_json, ov.samples_json, ov.generated_draft_json, ov.config_export_json, ov.wizard_state_json, ov.change_note, ov.created_at as \"created_at!: time::OffsetDateTime\", ov.created_by from operation_versions ov join operations o on o.id = ov.operation_id where o.workspace_id = $1 and ov.operation_id = $2 order by ov.version asc", workspace_id.as_str(), operation_id.as_str(), ) .fetch_all(&self.pool) .await?; rows.into_iter() .map(|row| { build_operation_version_record( row.id, row.workspace_id, row.name, row.display_name, row.category, row.protocol, row.security_level, row.operation_created_at, row.operation_updated_at, row.operation_published_at, row.version, row.status, row.target_json, row.input_schema_json, row.output_schema_json, row.input_mapping_json, row.output_mapping_json, row.execution_config_json, row.tool_description_json, row.samples_json, row.generated_draft_json, row.config_export_json, row.wizard_state_json, row.change_note, row.created_at, row.created_by, ) }) .collect() } pub async fn publish_operation( &self, request: PublishRequest<'_>, ) -> Result<(), RegistryError> { self.publish_operation_inner(request, None).await } pub async fn publish_operation_cas( &self, request: PublishRequest<'_>, expected_state: &OperationStateExpectation, ) -> Result<(), RegistryError> { self.publish_operation_inner(request, Some(expected_state)) .await } async fn publish_operation_inner( &self, request: PublishRequest<'_>, expected_state: Option<&OperationStateExpectation>, ) -> Result<(), RegistryError> { let mut tx = self.pool.begin().await?; let row = sqlx::query( "select status, current_draft_version, latest_published_version from operations where workspace_id = $1 and id = $2 for update", ) .bind(request.workspace_id.as_str()) .bind(request.operation_id.as_str()) .fetch_optional(&mut *tx) .await?; let Some(row) = row else { return Err(RegistryError::OperationNotFound { operation_id: request.operation_id.as_str().to_owned(), }); }; verify_expected_state(&row, request.operation_id.as_str(), expected_state)?; let aggregate_status: OperationStatus = deserialize_enum_text(&row.try_get::("status")?, "status")?; if aggregate_status == OperationStatus::Archived { return Err(RegistryError::OperationArchived { operation_id: request.operation_id.as_str().to_owned(), }); } let current = from_db_version( row.try_get::("current_draft_version")?, "current_draft_version", )?; let latest = row .try_get::, _>("latest_published_version")? .map(|value| from_db_version(value, "latest_published_version")) .transpose()?; if latest == Some(request.version) && current == request.version { tx.commit().await?; return Ok(()); } if request.version != current || latest.is_some_and(|latest| request.version <= latest) { return Err(RegistryError::OperationStaleVersion { operation_id: request.operation_id.as_str().to_owned(), expected: current, actual: request.version, }); } let version_status = sqlx::query( "select status, execution_config_json from operation_versions where operation_id = $1 and version = $2", ) .bind(request.operation_id.as_str()) .bind(to_db_version(request.version)) .fetch_optional(&mut *tx) .await?; let Some(version_row) = version_status else { return Err(RegistryError::OperationVersionNotFound { operation_id: request.operation_id.as_str().to_owned(), version: request.version, }); }; let version_status: OperationStatus = deserialize_enum_text(&version_row.try_get::("status")?, "status")?; if version_status != OperationStatus::Draft { return Err(RegistryError::InvalidOperationTransition { operation_id: request.operation_id.as_str().to_owned(), from: format!("{version_status:?}").to_ascii_lowercase(), action: "publish", }); } let execution_config: crank_core::ExecutionConfig = deserialize_json_value(version_row.try_get::("execution_config_json")?)?; if let Some(auth_profile_id) = execution_config .auth_profile_ref .as_ref() .filter(|_| expected_state.is_some()) { let available = sqlx::query_scalar::<_, i32>( "select 1 from auth_profiles where workspace_id = $1 and id = $2 for key share", ) .bind(request.workspace_id.as_str()) .bind(auth_profile_id.as_str()) .fetch_optional(&mut *tx) .await? .is_some(); if !available { return Err(RegistryError::OperationAuthProfileUnavailable { operation_id: request.operation_id.as_str().to_owned(), }); } } sqlx::query( "insert into published_operations ( operation_id, version, published_at, published_by ) values ($1, $2, $3::timestamptz, $4) on conflict(operation_id) do update set version = excluded.version, published_at = excluded.published_at, published_by = excluded.published_by where published_operations.version < excluded.version", ) .bind(request.operation_id.as_str()) .bind(to_db_version(request.version)) .bind(request.published_at) .bind(request.published_by) .execute(&mut *tx) .await?; sqlx::query( "update operation_versions set status = $1, published_at = $2::timestamptz, published_by = $3 where operation_id = $4 and version = $5", ) .bind(serialize_enum_text(&OperationStatus::Published, "status")?) .bind(request.published_at) .bind(request.published_by) .bind(request.operation_id.as_str()) .bind(to_db_version(request.version)) .execute(&mut *tx) .await?; sqlx::query( "update operations set status = $1, latest_published_version = $2, published_at = $3::timestamptz, updated_at = $4::timestamptz where id = $5 and workspace_id = $6", ) .bind(serialize_enum_text(&OperationStatus::Published, "status")?) .bind(to_db_version(request.version)) .bind(request.published_at) .bind(request.published_at) .bind(request.operation_id.as_str()) .bind(request.workspace_id.as_str()) .execute(&mut *tx) .await?; tx.commit().await?; Ok(()) } } fn verify_expected_state( row: &PgRow, operation_id: &str, expected: Option<&OperationStateExpectation>, ) -> Result<(), RegistryError> { let Some(expected) = expected else { return Ok(()); }; let current = from_db_version( row.try_get::("current_draft_version")?, "current_draft_version", )?; let status: OperationStatus = deserialize_enum_text(&row.try_get::("status")?, "status")?; let latest = row .try_get::, _>("latest_published_version")? .map(|value| from_db_version(value, "latest_published_version")) .transpose()?; if current != expected.current_draft_version || status != expected.status || latest != expected.latest_published_version { return Err(RegistryError::OperationStaleVersion { operation_id: operation_id.to_owned(), expected: expected.current_draft_version, actual: current, }); } Ok(()) } fn matches_operation_name_conflict(error: &RegistryError) -> bool { let RegistryError::Storage(sqlx::Error::Database(error)) = error else { return false; }; error.code().as_deref() == Some("23505") && error.constraint() == Some("operations_workspace_name_idx") }