Skip to main content

eliciter_core/
session.rs

1//! Session-configurable coverage schema (spec §6).
2//!
3//! Readiness requires every *required* coverage dimension to carry a
4//! `withstood`+ hypothesis. The schema is session-configurable; the default is
5//! the five canonical discovery dimensions.
6
7use serde::{Deserialize, Serialize};
8
9/// One coverage dimension the interview must satisfy.
10#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11pub struct CoverageDimension {
12    /// The dimension tag, matched by exact equality against `Claim.dimensions`.
13    pub tag: String,
14    /// Whether this dimension must be covered for readiness (spec §6.1).
15    #[serde(default = "default_true")]
16    pub required: bool,
17}
18
19fn default_true() -> bool {
20    true
21}
22
23impl CoverageDimension {
24    /// A required dimension with the given tag.
25    pub fn required(tag: impl Into<String>) -> Self {
26        Self {
27            tag: tag.into(),
28            required: true,
29        }
30    }
31}
32
33/// The session's coverage schema (spec §6). Default: the five canonical
34/// discovery dimensions, all required.
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct CoverageSchema {
37    pub dimensions: Vec<CoverageDimension>,
38}
39
40impl Default for CoverageSchema {
41    fn default() -> Self {
42        Self {
43            dimensions: [
44                "purpose",
45                "constraints",
46                "success-criteria",
47                "scope",
48                "non-goals",
49            ]
50            .iter()
51            .map(|t| CoverageDimension::required(*t))
52            .collect(),
53        }
54    }
55}
56
57impl CoverageSchema {
58    /// The tags of all required dimensions, in schema order.
59    pub fn required_tags(&self) -> impl Iterator<Item = &str> {
60        self.dimensions
61            .iter()
62            .filter(|d| d.required)
63            .map(|d| d.tag.as_str())
64    }
65}