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}