1use std::collections::HashMap;
4use std::time::SystemTime;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum Role {
9 Dev,
11 Qa,
13 System,
15}
16
17impl Role {
18 pub fn name(&self) -> &'static str {
20 match self {
21 Role::Dev => "Developer",
22 Role::Qa => "QA",
23 Role::System => "System",
24 }
25 }
26
27 pub fn can_claim(&self) -> bool {
29 matches!(self, Role::Dev)
30 }
31
32 pub fn can_verify(&self) -> bool {
34 matches!(self, Role::Qa)
35 }
36
37 pub fn can_approve(&self) -> bool {
39 matches!(self, Role::System)
40 }
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum VerificationResult {
46 Falsified,
48 Unfalsified,
50 Inconclusive,
52}
53
54impl VerificationResult {
55 pub fn should_approve(&self) -> bool {
57 matches!(self, VerificationResult::Unfalsified)
58 }
59}
60
61#[derive(Debug, Clone)]
63pub struct FalsificationCriterion {
64 pub id: String,
66 pub description: String,
68 pub pass_condition: String,
70}
71
72impl FalsificationCriterion {
73 pub fn new(id: &str, description: &str, pass_condition: &str) -> Self {
75 Self {
76 id: id.to_string(),
77 description: description.to_string(),
78 pass_condition: pass_condition.to_string(),
79 }
80 }
81
82 pub fn hash(&self) -> u64 {
84 let mut hash: u64 = 0;
86 for byte in self.id.bytes() {
87 hash = hash.wrapping_mul(31).wrapping_add(u64::from(byte));
88 }
89 for byte in self.description.bytes() {
90 hash = hash.wrapping_mul(31).wrapping_add(u64::from(byte));
91 }
92 for byte in self.pass_condition.bytes() {
93 hash = hash.wrapping_mul(31).wrapping_add(u64::from(byte));
94 }
95 hash
96 }
97}
98
99#[derive(Debug, Clone)]
101pub struct FalsificationClaim {
102 pub id: String,
104 pub feature: String,
106 pub criteria: Vec<FalsificationCriterion>,
108 pub criteria_hash: u64,
110 pub timestamp: SystemTime,
112 pub claimant: String,
114 pub version: String,
116 pub evidence: Vec<String>,
118}
119
120impl FalsificationClaim {
121 pub fn new(id: &str, feature: &str, claimant: &str, version: &str) -> Self {
123 Self {
124 id: id.to_string(),
125 feature: feature.to_string(),
126 criteria: Vec::new(),
127 criteria_hash: 0,
128 timestamp: SystemTime::now(),
129 claimant: claimant.to_string(),
130 version: version.to_string(),
131 evidence: Vec::new(),
132 }
133 }
134
135 pub fn add_criterion(&mut self, criterion: FalsificationCriterion) {
137 self.criteria.push(criterion);
138 self.update_hash();
139 }
140
141 pub fn add_evidence(&mut self, evidence: &str) {
143 self.evidence.push(evidence.to_string());
144 }
145
146 fn update_hash(&mut self) {
148 let mut hash: u64 = 0;
149 for criterion in &self.criteria {
150 hash = hash.wrapping_add(criterion.hash());
151 }
152 self.criteria_hash = hash;
153 }
154
155 pub fn verify_hash(&self) -> bool {
157 let mut expected: u64 = 0;
158 for criterion in &self.criteria {
159 expected = expected.wrapping_add(criterion.hash());
160 }
161 expected == self.criteria_hash
162 }
163
164 pub fn is_valid(&self) -> bool {
166 !self.id.is_empty()
167 && !self.feature.is_empty()
168 && !self.claimant.is_empty()
169 && !self.version.is_empty()
170 && !self.criteria.is_empty()
171 && self.verify_hash()
172 }
173}
174
175#[derive(Debug, Clone)]
177pub struct BlackBoxArtifact {
178 pub id: String,
180 pub binary_hash: String,
182 pub criteria: Vec<FalsificationCriterion>,
184 pub criteria_hash: u64,
186 pub version: String,
188 pub deadline: Option<SystemTime>,
190}
191
192impl BlackBoxArtifact {
193 pub fn from_claim(claim: &FalsificationClaim, binary_hash: &str) -> Self {
195 Self {
196 id: format!("BB-{}", claim.id),
197 binary_hash: binary_hash.to_string(),
198 criteria: claim.criteria.clone(),
199 criteria_hash: claim.criteria_hash,
200 version: claim.version.clone(),
201 deadline: None,
202 }
203 }
204
205 pub fn with_deadline(mut self, deadline: SystemTime) -> Self {
207 self.deadline = Some(deadline);
208 self
209 }
210
211 pub fn is_expired(&self) -> bool {
213 if let Some(deadline) = self.deadline {
214 SystemTime::now() > deadline
215 } else {
216 false
217 }
218 }
219
220 pub fn verify_criteria_integrity(&self, claim: &FalsificationClaim) -> bool {
222 self.criteria_hash == claim.criteria_hash
223 }
224}
225
226#[derive(Debug, Clone)]
228pub struct VerificationAttempt {
229 pub id: String,
231 pub artifact_id: String,
233 pub verifier: String,
235 pub result: VerificationResult,
237 pub timestamp: SystemTime,
239 pub evidence: Vec<String>,
241 pub criterion_results: HashMap<String, bool>,
243}
244
245impl VerificationAttempt {
246 pub fn new(id: &str, artifact_id: &str, verifier: &str) -> Self {
248 Self {
249 id: id.to_string(),
250 artifact_id: artifact_id.to_string(),
251 verifier: verifier.to_string(),
252 result: VerificationResult::Inconclusive,
253 timestamp: SystemTime::now(),
254 evidence: Vec::new(),
255 criterion_results: HashMap::new(),
256 }
257 }
258
259 pub fn record_criterion(&mut self, criterion_id: &str, passed: bool) {
261 self.criterion_results
262 .insert(criterion_id.to_string(), passed);
263 }
264
265 pub fn add_evidence(&mut self, evidence: &str) {
267 self.evidence.push(evidence.to_string());
268 }
269
270 pub fn finalize(&mut self, result: VerificationResult) {
272 self.result = result;
273 self.timestamp = SystemTime::now();
274 }
275
276 pub fn has_falsification(&self) -> bool {
278 self.criterion_results.values().any(|&passed| !passed)
279 }
280
281 pub fn passed_count(&self) -> usize {
283 self.criterion_results.values().filter(|&&p| p).count()
284 }
285
286 pub fn failed_count(&self) -> usize {
288 self.criterion_results.values().filter(|&&p| !p).count()
289 }
290}
291
292#[derive(Debug, Clone)]
294pub struct ScorecardComponent {
295 pub name: String,
297 pub weight: f64,
299 pub score: u32,
301}
302
303impl ScorecardComponent {
304 pub fn new(name: &str, weight: f64, score: u32) -> Self {
306 Self {
307 name: name.to_string(),
308 weight,
309 score: score.min(100),
310 }
311 }
312
313 pub fn weighted_score(&self) -> f64 {
315 self.weight * f64::from(self.score)
316 }
317}
318
319#[derive(Debug, Clone)]
321pub struct ScorecardV2 {
322 pub components: Vec<ScorecardComponent>,
324 pub version: u8,
326}
327
328impl Default for ScorecardV2 {
329 fn default() -> Self {
330 Self::new()
331 }
332}
333
334impl ScorecardV2 {
335 pub fn new() -> Self {
337 Self {
338 components: vec![
339 ScorecardComponent::new("Core Correctness", 0.30, 0),
340 ScorecardComponent::new("Performance", 0.30, 0),
341 ScorecardComponent::new("Resilience", 0.20, 0),
342 ScorecardComponent::new("Usability", 0.20, 0),
343 ],
344 version: 2,
345 }
346 }
347
348 pub fn set_score(&mut self, name: &str, score: u32) -> bool {
350 for component in &mut self.components {
351 if component.name == name {
352 component.score = score.min(100);
353 return true;
354 }
355 }
356 false
357 }
358
359 pub fn total_score(&self) -> f64 {
361 self.components.iter().map(|c| c.weighted_score()).sum()
362 }
363
364 pub fn weights_valid(&self) -> bool {
366 let sum: f64 = self.components.iter().map(|c| c.weight).sum();
367 (sum - 1.0).abs() < 1e-10
368 }
369
370 pub fn passes(&self) -> bool {
372 self.total_score() >= 70.0
373 }
374
375 pub fn grade(&self) -> &'static str {
377 let score = self.total_score();
378 if score >= 90.0 {
379 "A"
380 } else if score >= 80.0 {
381 "B"
382 } else if score >= 70.0 {
383 "C"
384 } else if score >= 60.0 {
385 "D"
386 } else {
387 "F"
388 }
389 }
390}
391
392#[derive(Debug, Clone, PartialEq, Eq)]
394pub enum ReleaseDecision {
395 Approved { reason: String },
397 Rejected { reason: String },
399 Pending { reason: String },
401}
402
403impl ReleaseDecision {
404 pub fn is_approved(&self) -> bool {
406 matches!(self, ReleaseDecision::Approved { .. })
407 }
408
409 pub fn reason(&self) -> &str {
411 match self {
412 ReleaseDecision::Approved { reason }
413 | ReleaseDecision::Rejected { reason }
414 | ReleaseDecision::Pending { reason } => reason,
415 }
416 }
417}
418
419#[derive(Debug, Clone)]
421pub struct AuditEntry {
422 pub id: String,
424 pub timestamp: SystemTime,
426 pub role: Role,
428 pub actor: String,
430 pub action: String,
432 pub artifacts: Vec<String>,
434}
435
436impl AuditEntry {
437 pub fn new(id: &str, role: Role, actor: &str, action: &str) -> Self {
439 Self {
440 id: id.to_string(),
441 timestamp: SystemTime::now(),
442 role,
443 actor: actor.to_string(),
444 action: action.to_string(),
445 artifacts: Vec::new(),
446 }
447 }
448
449 pub fn with_artifact(mut self, artifact_id: &str) -> Self {
451 self.artifacts.push(artifact_id.to_string());
452 self
453 }
454}
455
456#[derive(Debug, Clone, Copy, PartialEq, Eq)]
458pub enum SessionState {
459 AwaitingClaims,
461 AwaitingVerification,
463 AwaitingDecision,
465 Completed,
467}
468
469#[derive(Debug, Clone)]
471pub struct VerificationReport {
472 pub session_id: String,
474 pub total_claims: usize,
476 pub total_artifacts: usize,
478 pub total_attempts: usize,
480 pub falsified_count: usize,
482 pub unfalsified_count: usize,
484 pub inconclusive_count: usize,
486 pub scorecard_total: f64,
488 pub scorecard_grade: String,
490 pub audit_entries: usize,
492}
493
494impl VerificationReport {
495 pub fn is_success(&self) -> bool {
497 self.falsified_count == 0 && self.scorecard_total >= 70.0
498 }
499}