Add tool quality report contract
Deploy / deploy (push) Successful in 1m40s
CI / Rust Checks (push) Failing after 4m48s
CI / Frontend E2E (push) Has been skipped
CI / Deployment Manifests (push) Has been skipped
CI / UI Checks (push) Has been skipped

This commit is contained in:
github-ops
2026-06-20 19:50:46 +00:00
parent 906b6c1aea
commit 3b61d7a25d
8 changed files with 110 additions and 4 deletions
+2
View File
@@ -9,6 +9,7 @@ pub mod observability;
pub mod operation;
pub mod protocol;
pub mod secret;
pub mod tool_quality;
pub mod workspace;
pub use access::{
@@ -61,4 +62,5 @@ pub use operation::{
};
pub use protocol::{AuthKind, ExportMode, HttpMethod, Protocol};
pub use secret::{Secret, SecretKind, SecretStatus, SecretVersion};
pub use tool_quality::{ToolQualityFinding, ToolQualityReport, ToolQualitySeverity};
pub use workspace::{Workspace, WorkspaceStatus};
+40
View File
@@ -0,0 +1,40 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ToolQualitySeverity {
Info,
Warning,
Error,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolQualityFinding {
pub severity: ToolQualitySeverity,
pub code: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub suggested_action: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub field_path: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolQualityReport {
pub blocking: bool,
pub findings: Vec<ToolQualityFinding>,
}
impl ToolQualityReport {
pub fn new(findings: Vec<ToolQualityFinding>) -> Self {
let blocking = findings
.iter()
.any(|finding| finding.severity == ToolQualitySeverity::Error);
Self { blocking, findings }
}
pub fn empty() -> Self {
Self::new(Vec::new())
}
}
+3
View File
@@ -0,0 +1,3 @@
mod unit {
mod tool_quality;
}
@@ -0,0 +1,32 @@
use crank_core::{ToolQualityFinding, ToolQualityReport, ToolQualitySeverity};
#[test]
fn serializes_tool_quality_finding_contract() {
let finding = ToolQualityFinding {
severity: ToolQualitySeverity::Warning,
code: "tool_description_too_short".to_owned(),
message: "Описание инструмента слишком короткое.".to_owned(),
suggested_action: Some("Добавьте назначение и условия применения инструмента.".to_owned()),
field_path: Some("tool_description.description".to_owned()),
};
let value = serde_json::to_value(finding).unwrap();
assert_eq!(value["severity"], "warning");
assert_eq!(value["code"], "tool_description_too_short");
assert_eq!(value["field_path"], "tool_description.description");
}
#[test]
fn report_is_blocking_when_error_finding_exists() {
let report = ToolQualityReport::new(vec![ToolQualityFinding {
severity: ToolQualitySeverity::Error,
code: "tool_name_missing".to_owned(),
message: "Имя инструмента обязательно.".to_owned(),
suggested_action: None,
field_path: Some("name".to_owned()),
}]);
assert!(report.blocking);
assert_eq!(report.findings.len(), 1);
}