knowdit_kg_model/db/semantic_finding_link.rs
1//! KG-side `(semantic_node, audit_finding)` edge — one row per pair
2//! emitted by the link agent. The agent produces a strength label
3//! per pair AND a free-form evidence rationale; both land here. The
4//! composite primary key `(semantic_node_id, audit_finding_id)`
5//! enforces uniqueness without needing a separate UNIQUE index.
6//!
7//! See `plan_link.md` §5.1 and §6.2 for the rubric definition that
8//! drives the `strength` value.
9
10use sea_orm::entity::prelude::*;
11
12#[sea_orm::model]
13#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, DeriveEntityModel)]
14#[sea_orm(table_name = "semantic_finding_link")]
15pub struct Model {
16 #[sea_orm(primary_key, auto_increment = false)]
17 pub semantic_node_id: i32,
18 #[sea_orm(primary_key, auto_increment = false)]
19 pub audit_finding_id: i32,
20 /// Link agent's strength tier for this pair (High / Medium / Low).
21 /// Indexed so downstream read APIs can filter by strength.rank()
22 /// efficiently.
23 #[sea_orm(indexed)]
24 pub strength: crate::link_strength::LinkStrength,
25 /// Free-form rationale the agent emitted alongside the strength
26 /// label. Length floors are enforced at the agent tool handler
27 /// (40+ chars for High/Medium, 15+ for Low). Useful for human
28 /// audit and downstream prompt-building consumers.
29 #[sea_orm(column_type = "Text")]
30 pub evidence: String,
31
32 #[sea_orm(belongs_to, from = "semantic_node_id", to = "id")]
33 pub semantic_node: Option<super::semantic_node::Entity>,
34 #[sea_orm(belongs_to, from = "audit_finding_id", to = "id")]
35 pub audit_finding: Option<super::audit_finding::Entity>,
36}
37
38impl ActiveModelBehavior for ActiveModel {}