Skip to main content

dpm_model/
context.rs

1use crate::{ActorId, ArtifactId, DecisionId, Key, ProjectId, RequirementId, RiskId, WorkItemId};
2use chrono::{DateTime, Utc};
3use serde::{Deserialize, Serialize};
4use std::collections::{BTreeMap, BTreeSet};
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6/// Traceable motivation for one or more work items.
7#[serde(deny_unknown_fields)]
8pub struct Requirement {
9    /// Stable entity identity; it must match its containing map key.
10    pub id: RequirementId,
11    /// Human-readable key, unique in this entity category.
12    pub key: Key,
13    /// Project that owns this entity.
14    pub project: ProjectId,
15    /// Non-empty human-readable title.
16    pub title: String,
17    /// Requirement that the implementation must satisfy.
18    pub statement: String,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22/// Category of externally stored work evidence.
23pub enum ArtifactKind {
24    /// A Git commit.
25    GitCommit,
26    /// A code review request.
27    PullRequest,
28    /// An external file.
29    File,
30    /// A build output.
31    Build,
32    /// Acceptance or test evidence.
33    TestResult,
34    /// A component specification.
35    Datasheet,
36    /// A supplier quotation.
37    Quote,
38    /// A procurement order.
39    PurchaseOrder,
40    /// A design file.
41    Cad,
42    /// A photograph.
43    Photo,
44    /// Evidence outside the standard categories.
45    Other,
46}
47
48#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
49/// Reference to evidence with its creating principal and timestamp.
50#[serde(deny_unknown_fields)]
51pub struct Artifact {
52    /// Stable entity identity; it must match its containing map key.
53    pub id: ArtifactId,
54    /// Domain category of this value.
55    pub kind: ArtifactKind,
56    /// Non-empty locator of the evidence.
57    pub uri: String,
58    /// Human-readable evidence description.
59    pub label: String,
60    /// Artifact-specific attributes, such as a Git commit hash.
61    pub metadata: BTreeMap<String, String>,
62    /// Principal responsible for this evidence.
63    pub created_by: ActorId,
64    /// Timestamp at which the evidence was recorded.
65    pub created_at: DateTime<Utc>,
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
69/// Lifecycle of a recorded choice or question that can optionally gate execution.
70pub enum DecisionStatus {
71    /// Unresolved question.
72    Open,
73    /// Recorded choice with an outcome.
74    Decided,
75    /// Decision no longer applicable.
76    Superseded,
77}
78
79#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
80/// A question or choice included in work context, optionally blocking execution.
81#[serde(deny_unknown_fields)]
82pub struct Decision {
83    /// Stable entity identity; it must match its containing map key.
84    pub id: DecisionId,
85    /// Human-readable key, unique in this entity category.
86    pub key: Key,
87    /// Project that owns this entity.
88    pub project: ProjectId,
89    /// Question requiring a decision.
90    pub question: String,
91    /// Whether the question still gates execution.
92    pub status: DecisionStatus,
93    /// Non-empty resolution for a decided gate.
94    pub outcome: Option<String>,
95    /// When `decide`, or the reviewed change that created this replacement, resolved it; absent for
96    /// decisions resolved before this was recorded.
97    #[serde(default, skip_serializing_if = "Option::is_none")]
98    pub resolved_at: Option<DateTime<Utc>>,
99    /// Reason for the choice or for raising the question.
100    #[serde(default, skip_serializing_if = "Option::is_none")]
101    pub rationale: Option<String>,
102    /// Work whose context includes this decision, without imposing an execution gate.
103    #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
104    pub related_work: BTreeSet<WorkItemId>,
105    /// Sources supporting the decision, distinct from task completion evidence.
106    #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
107    pub artifact_ids: BTreeSet<ArtifactId>,
108    /// Work items gated by this decision.
109    pub blocks: BTreeSet<WorkItemId>,
110    /// Superseded decision whose rationale and sources this choice replaces.
111    #[serde(default, skip_serializing_if = "Option::is_none")]
112    pub supersedes: Option<DecisionId>,
113    /// Structured alternatives; when present, a decided outcome is exactly one option key.
114    #[serde(default, skip_serializing_if = "Vec::is_empty")]
115    pub options: Vec<crate::DecisionOption>,
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
119/// Qualitative consequence of a risk.
120pub enum RiskImpact {
121    /// Small consequence.
122    Low,
123    /// Moderate consequence.
124    Medium,
125    /// Large consequence.
126    High,
127    /// Project-threatening consequence.
128    Critical,
129}
130
131#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
132/// Identified uncertainty with probability and related work.
133#[serde(deny_unknown_fields)]
134pub struct Risk {
135    /// Stable entity identity; it must match its containing map key.
136    pub id: RiskId,
137    /// Human-readable key, unique in this entity category.
138    pub key: Key,
139    /// Project that owns this entity.
140    pub project: ProjectId,
141    /// Risk event or condition.
142    pub description: String,
143    /// Probability in [0, 1].
144    pub probability: f64,
145    /// Qualitative consequence if the risk occurs.
146    pub impact: RiskImpact,
147    /// Work affected by the risk.
148    pub related_work: BTreeSet<WorkItemId>,
149    /// Optional response to reduce the risk.
150    pub mitigation: Option<String>,
151}