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}