1use crate::types::*;
2use crate::{Error, PlanningEngine, Result};
3use uuid::Uuid;
4
5impl PlanningEngine {
6 pub fn create_goal(&mut self, request: CreateGoalRequest) -> Result<Goal> {
7 let id = GoalId(Uuid::new_v4());
8 let now = Timestamp::now();
9 let urgency = self.calculate_initial_urgency(&request);
10 let gravity = self.calculate_initial_gravity(&request);
11 let inertia = self.calculate_initial_inertia(&request);
12
13 let soul = GoalSoul {
14 intention: request.intention.clone(),
15 significance: request.significance.unwrap_or_default(),
16 success_criteria: request.success_criteria.unwrap_or_default(),
17 emotional_weight: request.emotional_weight.unwrap_or(0.5),
18 values: request.values.unwrap_or_default(),
19 };
20
21 let feelings = GoalFeelings {
22 urgency,
23 neglect: 0.0,
24 confidence: 0.5,
25 alignment: 1.0,
26 vitality: 1.0,
27 last_calculated: now,
28 };
29
30 let physics = GoalPhysics {
31 momentum: 0.0,
32 gravity,
33 inertia,
34 energy: 1.0,
35 last_calculated: now,
36 };
37
38 let goal = Goal {
39 id,
40 title: request.title,
41 description: request.description,
42 soul,
43 status: GoalStatus::Draft,
44 created_at: now,
45 activated_at: None,
46 completed_at: None,
47 deadline: request.deadline,
48 parent: request.parent,
49 children: Vec::new(),
50 dependencies: request.dependencies.unwrap_or_default(),
51 dependents: Vec::new(),
52 relationships: Vec::new(),
53 priority: request.priority.unwrap_or(Priority::Medium),
54 progress: Progress::new(),
55 feelings,
56 physics,
57 blockers: Vec::new(),
58 decisions: Vec::new(),
59 commitments: Vec::new(),
60 dreams: Vec::new(),
61 tags: request.tags.unwrap_or_default(),
62 metadata: request.metadata.unwrap_or_default(),
63 provenance: GoalProvenance {
64 origin: request.origin.unwrap_or(ProvenanceOrigin::UserRequest),
65 user_request: request.user_request,
66 session_id: request.session_id,
67 creation_context: request.context.unwrap_or_default(),
68 },
69 metamorphosis: None,
70 previous_life: None,
71 };
72
73 self.goal_store.insert(id, goal.clone());
74
75 if let Some(parent_id) = request.parent {
76 if let Some(parent) = self.goal_store.get_mut(&parent_id) {
77 parent.children.push(id);
78 }
79 }
80
81 for dep_id in &goal.dependencies {
82 if let Some(dep) = self.goal_store.get_mut(dep_id) {
83 dep.dependents.push(id);
84 }
85 }
86
87 self.indexes.add_goal(&goal);
88 self.mark_dirty();
89 Ok(goal)
90 }
91
92 pub fn activate_goal(&mut self, id: GoalId) -> Result<Goal> {
93 let out = {
94 let goal = self
95 .goal_store
96 .get_mut(&id)
97 .ok_or(Error::GoalNotFound(id))?;
98 if goal.status != GoalStatus::Draft && goal.status != GoalStatus::Reborn {
99 return Err(Error::InvalidTransition {
100 from: goal.status,
101 to: GoalStatus::Active,
102 });
103 }
104
105 let old = goal.status;
106 goal.status = GoalStatus::Active;
107 goal.activated_at = Some(Timestamp::now());
108 goal.feelings.vitality = 1.0;
109 self.indexes
110 .goal_status_changed(id, old, GoalStatus::Active);
111 goal.clone()
112 };
113 self.mark_dirty();
114 Ok(out)
115 }
116
117 pub fn pause_goal(&mut self, id: GoalId, reason: Option<String>) -> Result<Goal> {
118 let out = {
119 let goal = self
120 .goal_store
121 .get_mut(&id)
122 .ok_or(Error::GoalNotFound(id))?;
123 let old = goal.status;
124 goal.status = GoalStatus::Paused;
125 if let Some(r) = reason {
126 goal.metadata
127 .insert("pause_reason".to_string(), serde_json::Value::String(r));
128 goal.metadata.insert(
129 "paused_at".to_string(),
130 serde_json::Value::String(Timestamp::now().0.to_string()),
131 );
132 }
133 self.indexes
134 .goal_status_changed(id, old, GoalStatus::Paused);
135 goal.clone()
136 };
137 self.mark_dirty();
138 Ok(out)
139 }
140
141 pub fn resume_goal(&mut self, id: GoalId) -> Result<Goal> {
142 let out = {
143 let goal = self
144 .goal_store
145 .get_mut(&id)
146 .ok_or(Error::GoalNotFound(id))?;
147 let old = goal.status;
148 goal.status = GoalStatus::Active;
149 self.indexes
150 .goal_status_changed(id, old, GoalStatus::Active);
151 goal.clone()
152 };
153 self.mark_dirty();
154 Ok(out)
155 }
156
157 pub fn progress_goal(
158 &mut self,
159 id: GoalId,
160 percentage: f64,
161 note: Option<String>,
162 ) -> Result<Goal> {
163 let now = Timestamp::now();
164 let snapshot = {
165 let goal = self
166 .goal_store
167 .get_mut(&id)
168 .ok_or(Error::GoalNotFound(id))?;
169 let p = percentage.clamp(0.0, 1.0);
170 goal.progress.history.push(ProgressPoint {
171 timestamp: now,
172 percentage: p,
173 note,
174 });
175 goal.progress.percentage = p;
176 goal.clone()
177 };
178
179 let velocity = self.calculate_velocity(&snapshot.progress.history);
180 let momentum = self.calculate_momentum_from_goal(&snapshot);
181 let confidence = self.calculate_confidence_from_goal(&snapshot);
182
183 {
184 let goal = self
185 .goal_store
186 .get_mut(&id)
187 .ok_or(Error::GoalNotFound(id))?;
188 goal.progress.velocity = velocity;
189 if velocity > 0.0 {
190 let remaining = 1.0 - goal.progress.percentage;
191 let days_remaining = remaining / velocity;
192 goal.progress.eta = Some(Timestamp::days_from_now(days_remaining));
193 }
194 goal.physics.momentum = momentum;
195 goal.feelings.neglect = 0.0;
196 goal.feelings.confidence = confidence;
197 goal.feelings.last_calculated = now;
198
199 if goal.progress.percentage >= 1.0 {
200 for c in &mut goal.soul.success_criteria {
201 c.achieved = true;
202 c.achieved_at = Some(now);
203 }
204 }
205 }
206
207 self.mark_dirty();
208 Ok(self
209 .goal_store
210 .get(&id)
211 .ok_or(Error::GoalNotFound(id))?
212 .clone())
213 }
214
215 pub fn complete_goal(&mut self, id: GoalId, note: Option<String>) -> Result<Goal> {
216 let now = Timestamp::now();
217 {
218 let goal = self
219 .goal_store
220 .get_mut(&id)
221 .ok_or(Error::GoalNotFound(id))?;
222 if !matches!(
223 goal.status,
224 GoalStatus::Active | GoalStatus::Blocked | GoalStatus::Paused
225 ) {
226 return Err(Error::CannotComplete(goal.status));
227 }
228 let old = goal.status;
229 goal.status = GoalStatus::Completed;
230 goal.completed_at = Some(now);
231 goal.progress.percentage = 1.0;
232 goal.progress.history.push(ProgressPoint {
233 timestamp: now,
234 percentage: 1.0,
235 note,
236 });
237 self.indexes
238 .goal_status_changed(id, old, GoalStatus::Completed);
239 }
240
241 let goal = self.goal_store.get(&id).ok_or(Error::GoalNotFound(id))?;
243 let karma = GoalKarma {
244 failures: Vec::new(),
245 near_successes: Vec::new(),
246 requirements_for_success: goal
247 .soul
248 .success_criteria
249 .iter()
250 .filter(|c| c.achieved)
251 .map(|c| c.description.clone())
252 .collect(),
253 invested_energy: goal.physics.energy,
254 };
255 let reincarnation_potential = self.calculate_reincarnation_potential(goal);
256 let soul_archive = GoalSoulArchive {
257 original_id: id,
258 soul: goal.soul.clone(),
259 death_record: GoalDeath {
260 cause: "completed".to_string(),
261 timestamp: now,
262 },
263 karma,
264 reincarnation_potential,
265 trigger_conditions: vec!["similar goal created".to_string()],
266 };
267 self.soul_archive.insert(id, soul_archive);
268
269 self.release_completion_energy(id);
270 let dependents = self
271 .goal_store
272 .get(&id)
273 .map(|g| g.dependents.clone())
274 .unwrap_or_default();
275 for dep in dependents {
276 self.check_unblock(&dep);
277 }
278
279 self.mark_dirty();
280 Ok(self
281 .goal_store
282 .get(&id)
283 .ok_or(Error::GoalNotFound(id))?
284 .clone())
285 }
286
287 pub fn abandon_goal(&mut self, id: GoalId, reason: String) -> Result<Goal> {
288 let now = Timestamp::now();
289 let snapshot = self
290 .goal_store
291 .get(&id)
292 .ok_or(Error::GoalNotFound(id))?
293 .clone();
294
295 let karma = GoalKarma {
296 failures: vec![reason.clone()],
297 near_successes: Vec::new(),
298 requirements_for_success: Vec::new(),
299 invested_energy: snapshot.progress.percentage,
300 };
301
302 self.soul_archive.insert(
303 id,
304 GoalSoulArchive {
305 original_id: id,
306 soul: snapshot.soul.clone(),
307 death_record: GoalDeath {
308 cause: reason,
309 timestamp: now,
310 },
311 karma,
312 reincarnation_potential: self.calculate_reincarnation_potential(&snapshot),
313 trigger_conditions: Vec::new(),
314 },
315 );
316
317 if let Some(goal) = self.goal_store.get_mut(&id) {
318 let old = goal.status;
319 goal.status = GoalStatus::Abandoned;
320 goal.completed_at = Some(now);
321 goal.physics.momentum = 0.0;
322 goal.physics.energy = 0.0;
323 self.indexes
324 .goal_status_changed(id, old, GoalStatus::Abandoned);
325 }
326
327 self.mark_dirty();
328 Ok(self
329 .goal_store
330 .get(&id)
331 .ok_or(Error::GoalNotFound(id))?
332 .clone())
333 }
334
335 pub fn reincarnate_goal(
336 &mut self,
337 original_id: GoalId,
338 updates: ReincarnationUpdates,
339 ) -> Result<Goal> {
340 let archive = self
341 .soul_archive
342 .get(&original_id)
343 .ok_or(Error::SoulNotFound(original_id))?
344 .clone();
345
346 let request = CreateGoalRequest {
347 title: updates
348 .title
349 .unwrap_or_else(|| format!("{} (Reborn)", archive.soul.intention)),
350 description: updates.description.unwrap_or_default(),
351 intention: archive.soul.intention.clone(),
352 significance: Some(archive.soul.significance.clone()),
353 success_criteria: Some(archive.soul.success_criteria.clone()),
354 emotional_weight: Some(archive.soul.emotional_weight),
355 values: Some(archive.soul.values.clone()),
356 origin: Some(ProvenanceOrigin::Reincarnation {
357 previous: original_id,
358 }),
359 ..Default::default()
360 };
361
362 let mut goal = self.create_goal(request)?;
363 goal.previous_life = Some(PreviousLife {
364 original_id,
365 death_cause: archive.death_record.cause,
366 lessons_learned: updates.lessons_learned.unwrap_or_default(),
367 karma: archive.karma,
368 });
369 goal.status = GoalStatus::Reborn;
370 self.goal_store.insert(goal.id, goal.clone());
371
372 self.mark_dirty();
373 Ok(goal)
374 }
375
376 pub fn decompose_goal(
377 &mut self,
378 id: GoalId,
379 sub_goals: Vec<CreateGoalRequest>,
380 ) -> Result<Vec<Goal>> {
381 let _ = self.goal_store.get(&id).ok_or(Error::GoalNotFound(id))?;
382 let mut created = Vec::new();
383
384 for mut request in sub_goals {
385 request.parent = Some(id);
386 request.origin = Some(ProvenanceOrigin::Decomposition { parent: id });
387 let child = self.create_goal(request)?;
388 created.push(child);
389 }
390
391 Ok(created)
392 }
393
394 pub fn block_goal(&mut self, id: GoalId, blocker: Blocker) -> Result<Goal> {
395 let mut snapshot = self
396 .goal_store
397 .get(&id)
398 .ok_or(Error::GoalNotFound(id))?
399 .clone();
400 snapshot.blockers.push(blocker);
401 snapshot.feelings.confidence = self.calculate_confidence_from_goal(&snapshot);
402
403 if let Some(goal) = self.goal_store.get_mut(&id) {
404 goal.blockers = snapshot.blockers;
405 goal.feelings.confidence = snapshot.feelings.confidence;
406 if goal.status == GoalStatus::Active {
407 goal.status = GoalStatus::Blocked;
408 self.indexes.goal_blocked(id);
409 }
410 }
411
412 self.mark_dirty();
413 Ok(self
414 .goal_store
415 .get(&id)
416 .ok_or(Error::GoalNotFound(id))?
417 .clone())
418 }
419
420 pub fn unblock_goal(
421 &mut self,
422 id: GoalId,
423 blocker_id: Uuid,
424 resolution: String,
425 ) -> Result<Goal> {
426 let now = Timestamp::now();
427 {
428 let goal = self
429 .goal_store
430 .get_mut(&id)
431 .ok_or(Error::GoalNotFound(id))?;
432 for blocker in &mut goal.blockers {
433 if blocker.id == blocker_id {
434 blocker.resolved_at = Some(now);
435 blocker.resolution = Some(resolution.clone());
436 }
437 }
438
439 let all_resolved = goal.blockers.iter().all(|b| b.resolved_at.is_some());
440 if all_resolved && goal.status == GoalStatus::Blocked {
441 goal.status = GoalStatus::Active;
442 self.indexes.goal_unblocked(id);
443 }
444 }
445
446 let snapshot = self
447 .goal_store
448 .get(&id)
449 .ok_or(Error::GoalNotFound(id))?
450 .clone();
451 let confidence = self.calculate_confidence_from_goal(&snapshot);
452 if let Some(goal) = self.goal_store.get_mut(&id) {
453 goal.feelings.confidence = confidence;
454 }
455
456 self.mark_dirty();
457 Ok(self
458 .goal_store
459 .get(&id)
460 .ok_or(Error::GoalNotFound(id))?
461 .clone())
462 }
463
464 pub fn link_goals(&mut self, relationship: GoalRelationship) -> Result<()> {
465 match &relationship {
466 GoalRelationship::Alliance { goals: (a, b), .. }
467 | GoalRelationship::Rivalry { goals: (a, b), .. }
468 | GoalRelationship::Romance { goals: (a, b), .. }
469 | GoalRelationship::Nemesis { goals: (a, b), .. } => {
470 if let Some(goal_a) = self.goal_store.get_mut(a) {
471 goal_a.relationships.push(relationship.clone());
472 }
473 if let Some(goal_b) = self.goal_store.get_mut(b) {
474 goal_b.relationships.push(relationship.clone());
475 }
476 }
477 GoalRelationship::Dependency { dependent, on, .. } => {
478 if let Some(dep) = self.goal_store.get_mut(dependent) {
479 dep.dependencies.push(*on);
480 }
481 if let Some(target) = self.goal_store.get_mut(on) {
482 target.dependents.push(*dependent);
483 }
484 }
485 GoalRelationship::ParentChild { parent, child } => {
486 if let Some(parent_goal) = self.goal_store.get_mut(parent) {
487 parent_goal.children.push(*child);
488 }
489 if let Some(child_goal) = self.goal_store.get_mut(child) {
490 child_goal.parent = Some(*parent);
491 }
492 }
493 GoalRelationship::Successor {
494 predecessor,
495 successor,
496 } => {
497 if let Some(p) = self.goal_store.get_mut(predecessor) {
498 p.relationships.push(relationship.clone());
499 }
500 if let Some(s) = self.goal_store.get_mut(successor) {
501 s.relationships.push(relationship.clone());
502 }
503 }
504 }
505
506 self.indexes.goal_linked(&relationship);
507 self.mark_dirty();
508 Ok(())
509 }
510
511 pub fn create_decision(&mut self, request: CreateDecisionRequest) -> Result<Decision> {
512 let id = DecisionId(Uuid::new_v4());
513 let now = Timestamp::now();
514
515 let decision = Decision {
516 id,
517 question: DecisionQuestion {
518 question: request.question,
519 context: request.context.unwrap_or_default(),
520 constraints: request.constraints.unwrap_or_default(),
521 asked_at: now,
522 },
523 status: DecisionStatus::Pending,
524 crystallized_at: None,
525 chosen: None,
526 shadows: Vec::new(),
527 reasoning: DecisionReasoning::default(),
528 decider: request.decider.unwrap_or(Decider::User { name: None }),
529 affected_goals: request.goals.unwrap_or_default(),
530 caused_by: request.caused_by,
531 causes: Vec::new(),
532 reversibility: Reversibility::default(),
533 consequences: Vec::new(),
534 regret_score: 0.0,
535 regret_updated_at: None,
536 };
537
538 for goal_id in &decision.affected_goals {
539 if let Some(goal) = self.goal_store.get_mut(goal_id) {
540 goal.decisions.push(id);
541 }
542 }
543
544 if let Some(parent_id) = request.caused_by {
545 if let Some(parent) = self.decision_store.get_mut(&parent_id) {
546 parent.causes.push(id);
547 }
548 }
549
550 self.decision_store.insert(id, decision.clone());
551 self.indexes.add_decision(&decision);
552 self.mark_dirty();
553 Ok(decision)
554 }
555
556 pub fn add_option(&mut self, id: DecisionId, path: DecisionPath) -> Result<Decision> {
557 let out = {
558 let decision = self
559 .decision_store
560 .get_mut(&id)
561 .ok_or(Error::DecisionNotFound(id))?;
562
563 if decision.status == DecisionStatus::Crystallized {
564 return Err(Error::AlreadyCrystallized);
565 }
566
567 decision.status = DecisionStatus::Deliberating;
568 decision.shadows.push(CrystalShadow {
569 path,
570 rejection_reason: String::new(),
571 counterfactual: None,
572 resurrection_cost: 0.0,
573 });
574 decision.clone()
575 };
576
577 self.mark_dirty();
578 Ok(out)
579 }
580
581 pub fn crystallize(
582 &mut self,
583 id: DecisionId,
584 chosen_path_id: PathId,
585 reasoning: DecisionReasoning,
586 ) -> Result<Decision> {
587 let now = Timestamp::now();
588 let snapshot = self
589 .decision_store
590 .get(&id)
591 .ok_or(Error::DecisionNotFound(id))?
592 .clone();
593
594 if snapshot.status == DecisionStatus::Crystallized {
595 return Err(Error::AlreadyCrystallized);
596 }
597
598 let chosen_idx = snapshot
599 .shadows
600 .iter()
601 .position(|s| s.path.id == chosen_path_id)
602 .ok_or(Error::PathNotFound(chosen_path_id))?;
603
604 let mut chosen_shadow = snapshot.shadows[chosen_idx].clone();
605 let mut new_shadows = Vec::new();
606
607 for (idx, shadow) in snapshot.shadows.iter().enumerate() {
608 if idx == chosen_idx {
609 continue;
610 }
611 let mut s = shadow.clone();
612 if s.rejection_reason.is_empty() {
613 s.rejection_reason = "Not chosen".to_string();
614 }
615 s.resurrection_cost = self.calculate_resurrection_cost(&snapshot, &s);
616 new_shadows.push(s);
617 }
618
619 let reversibility = self.calculate_reversibility(&snapshot);
620
621 if let Some(decision) = self.decision_store.get_mut(&id) {
622 chosen_shadow.resurrection_cost = 0.0;
623 decision.chosen = Some(chosen_shadow.path);
624 decision.shadows = new_shadows;
625 decision.status = DecisionStatus::Crystallized;
626 decision.crystallized_at = Some(now);
627 decision.reasoning = reasoning;
628 decision.reversibility = reversibility;
629 }
630
631 self.indexes.decision_crystallized(id);
632 self.mark_dirty();
633 Ok(self
634 .decision_store
635 .get(&id)
636 .ok_or(Error::DecisionNotFound(id))?
637 .clone())
638 }
639
640 pub fn record_consequence(
641 &mut self,
642 id: DecisionId,
643 consequence: Consequence,
644 ) -> Result<Decision> {
645 let snapshot = self
646 .decision_store
647 .get(&id)
648 .ok_or(Error::DecisionNotFound(id))?
649 .clone();
650
651 let mut updated = snapshot.clone();
652 updated.consequences.push(consequence);
653 updated.regret_score = self.calculate_regret(&updated);
654 updated.regret_updated_at = Some(Timestamp::now());
655 if updated.regret_score > 0.7 {
656 updated.status = DecisionStatus::Regretted;
657 }
658
659 self.decision_store.insert(id, updated.clone());
660 self.mark_dirty();
661 Ok(updated)
662 }
663
664 pub fn recrystallize(
665 &mut self,
666 id: DecisionId,
667 new_path_id: PathId,
668 reason: String,
669 ) -> Result<Decision> {
670 let mut decision = self
671 .decision_store
672 .get(&id)
673 .ok_or(Error::DecisionNotFound(id))?
674 .clone();
675
676 if decision.status != DecisionStatus::Crystallized
677 && decision.status != DecisionStatus::Regretted
678 {
679 return Err(Error::CannotRecrystallize(decision.status));
680 }
681
682 if let Some(old_chosen) = decision.chosen.take() {
683 decision.shadows.push(CrystalShadow {
684 path: old_chosen,
685 rejection_reason: format!("Recrystallized: {}", reason),
686 counterfactual: None,
687 resurrection_cost: 0.0,
688 });
689 }
690
691 let new_idx = decision
692 .shadows
693 .iter()
694 .position(|s| s.path.id == new_path_id)
695 .ok_or(Error::PathNotFound(new_path_id))?;
696
697 let new_chosen = decision.shadows.remove(new_idx);
698 decision.chosen = Some(new_chosen.path);
699 decision.status = DecisionStatus::Recrystallized;
700 decision.crystallized_at = Some(Timestamp::now());
701 decision.regret_score = 0.0;
702 decision.reasoning.rationale = format!(
703 "Recrystallized: {}. Previous: {}",
704 reason, decision.reasoning.rationale
705 );
706 decision
707 .reasoning
708 .factors_considered
709 .push(format!("Recrystallization reason: {}", reason));
710
711 self.decision_store.insert(id, decision.clone());
712 self.mark_dirty();
713 Ok(decision)
714 }
715
716 pub fn create_commitment(&mut self, request: CreateCommitmentRequest) -> Result<Commitment> {
717 let id = CommitmentId(Uuid::new_v4());
718 let now = Timestamp::now();
719 let weight = self.calculate_commitment_weight(&request);
720 let inertia = self.calculate_commitment_inertia(&request);
721 let breaking_cost = self.calculate_breaking_cost(&request);
722
723 let commitment = Commitment {
724 id,
725 promise: request.promise,
726 made_to: request.stakeholder,
727 made_at: now,
728 due: request.due,
729 status: CommitmentStatus::Active,
730 weight,
731 inertia,
732 breaking_cost,
733 goal: request.goal,
734 entanglements: Vec::new(),
735 fulfillment: None,
736 renegotiations: Vec::new(),
737 };
738
739 if let Some(goal_id) = request.goal {
740 if let Some(goal) = self.goal_store.get_mut(&goal_id) {
741 goal.commitments.push(id);
742 }
743 }
744
745 self.commitment_store.insert(id, commitment.clone());
746 self.indexes.add_commitment(&commitment);
747 self.mark_dirty();
748 Ok(commitment)
749 }
750
751 pub fn fulfill_commitment(
752 &mut self,
753 id: CommitmentId,
754 how_delivered: String,
755 ) -> Result<Commitment> {
756 let now = Timestamp::now();
757
758 let chain_bonus = self.calculate_chain_bonus(id);
759 let (status, weight, entanglements) = {
760 let c = self
761 .commitment_store
762 .get(&id)
763 .ok_or(Error::CommitmentNotFound(id))?;
764 (c.status, c.weight, c.entanglements.clone())
765 };
766
767 if status != CommitmentStatus::Active {
768 return Err(Error::CannotFulfill(status));
769 }
770
771 let energy_released = weight * (1.0 + chain_bonus);
772 if let Some(c) = self.commitment_store.get_mut(&id) {
773 c.status = CommitmentStatus::Fulfilled;
774 c.fulfillment = Some(CommitmentFulfillment {
775 fulfilled_at: now,
776 how_delivered,
777 energy_released,
778 trust_gained: c.weight * 0.5,
779 });
780 }
781
782 for entanglement in &entanglements {
783 if entanglement.entanglement_type == EntanglementType::Sequential {
784 let _ = self.boost_commitment(entanglement.with, energy_released * 0.3);
785 }
786 }
787
788 self.indexes.commitment_fulfilled(id);
789 self.mark_dirty();
790 Ok(self
791 .commitment_store
792 .get(&id)
793 .ok_or(Error::CommitmentNotFound(id))?
794 .clone())
795 }
796
797 pub fn break_commitment(&mut self, id: CommitmentId, reason: String) -> Result<Commitment> {
798 let commitment_ref = self
799 .commitment_store
800 .get(&id)
801 .ok_or(Error::CommitmentNotFound(id))?;
802 if matches!(
803 commitment_ref.status,
804 CommitmentStatus::Fulfilled | CommitmentStatus::Broken
805 ) {
806 return Err(Error::CannotBreak(commitment_ref.status));
807 }
808 let entanglements = commitment_ref.entanglements.clone();
809
810 if let Some(commitment) = self.commitment_store.get_mut(&id) {
811 commitment.status = CommitmentStatus::Broken;
812 commitment.renegotiations.push(Renegotiation {
814 renegotiated_at: Timestamp::now(),
815 original: commitment.promise.clone(),
816 new: Promise::default(),
817 reason,
818 accepted: false,
819 trust_impact: -commitment.breaking_cost.trust_damage,
820 });
821 }
822
823 for entanglement in entanglements {
824 if entanglement.entanglement_type == EntanglementType::Parallel {
825 let _ = self.destabilize_commitment(entanglement.with);
826 }
827 }
828
829 self.indexes.commitment_broken(id);
830 self.mark_dirty();
831 Ok(self
832 .commitment_store
833 .get(&id)
834 .ok_or(Error::CommitmentNotFound(id))?
835 .clone())
836 }
837
838 pub fn renegotiate_commitment(
839 &mut self,
840 id: CommitmentId,
841 new_promise: Promise,
842 reason: String,
843 ) -> Result<Commitment> {
844 let now = Timestamp::now();
845 let out = {
846 let commitment = self
847 .commitment_store
848 .get_mut(&id)
849 .ok_or(Error::CommitmentNotFound(id))?;
850
851 let old_promise = commitment.promise.clone();
852
853 let old_deliverable_count = old_promise.deliverables.len().max(1);
855 let new_deliverable_count = new_promise.deliverables.len().max(1);
856 let deliverable_change_ratio =
857 (new_deliverable_count as f64 - old_deliverable_count as f64).abs()
858 / old_deliverable_count as f64;
859
860 let prior_count = commitment.renegotiations.len();
862 let fatigue_penalty = (prior_count as f64 * 0.03).min(0.2);
863
864 let accepted = deliverable_change_ratio <= 0.5;
866
867 let trust_impact = if accepted {
869 -(0.02 + deliverable_change_ratio * 0.08 + fatigue_penalty)
870 } else {
871 -(0.1 + fatigue_penalty)
872 };
873
874 commitment.renegotiations.push(Renegotiation {
875 renegotiated_at: now,
876 original: old_promise,
877 new: new_promise.clone(),
878 reason,
879 accepted,
880 trust_impact,
881 });
882
883 if accepted {
884 commitment.promise = new_promise;
885 commitment.status = CommitmentStatus::Active;
886 } else {
887 commitment.status = CommitmentStatus::AtRisk;
888 }
889 commitment.clone()
890 };
891 self.mark_dirty();
892 Ok(out)
893 }
894
895 pub fn entangle_commitments(
896 &mut self,
897 a: CommitmentId,
898 b: CommitmentId,
899 entanglement_type: EntanglementType,
900 strength: f64,
901 ) -> Result<()> {
902 if let Some(ca) = self.commitment_store.get_mut(&a) {
903 ca.entanglements.push(CommitmentEntanglement {
904 with: b,
905 entanglement_type,
906 strength,
907 });
908 }
909
910 if let Some(cb) = self.commitment_store.get_mut(&b) {
911 cb.entanglements.push(CommitmentEntanglement {
912 with: a,
913 entanglement_type,
914 strength,
915 });
916 }
917
918 self.mark_dirty();
919 Ok(())
920 }
921
922 pub fn dream_goal(&mut self, id: GoalId) -> Result<Dream> {
923 let goal = self
924 .goal_store
925 .get(&id)
926 .ok_or(Error::GoalNotFound(id))?
927 .clone();
928
929 let dream_id = DreamId(Uuid::new_v4());
930 let scenario = self.generate_completion_scenario(&goal);
931 let obstacles = self.predict_obstacles(&goal);
932 let insights = self.extract_insights(&goal, &scenario, &obstacles);
933 let discovered_goals = self.discover_sub_goals(&goal, &obstacles);
934
935 let dream = Dream {
936 id: dream_id,
937 goal_id: id,
938 dreamt_at: Timestamp::now(),
939 scenario,
940 obstacles,
941 insights,
942 discovered_goals: discovered_goals.clone(),
943 confidence: self.calculate_dream_confidence(&goal),
944 accuracy: None,
945 };
946
947 if let Some(goal_mut) = self.goal_store.get_mut(&id) {
948 goal_mut.dreams.push(dream_id);
949 }
950
951 for seed in discovered_goals {
952 if dream.confidence > 0.7 {
953 let request = CreateGoalRequest {
954 title: seed.title,
955 description: seed.description,
956 parent: Some(id),
957 intention: seed.reason,
958 origin: Some(ProvenanceOrigin::Dream { dream: dream_id }),
959 ..Default::default()
960 };
961 let _ = self.create_goal(request);
962 }
963 }
964
965 self.dream_store.insert(dream_id, dream.clone());
966 self.mark_dirty();
967 Ok(dream)
968 }
969
970 pub fn create_federation(
971 &mut self,
972 goal_id: GoalId,
973 agent_id: String,
974 coordinator: Option<String>,
975 ) -> Result<Federation> {
976 let goal = self
977 .goal_store
978 .get(&goal_id)
979 .ok_or(Error::GoalNotFound(goal_id))?
980 .clone();
981
982 let now = Timestamp::now();
983 let id = FederationId(Uuid::new_v4());
984 let coordinator_id = coordinator.unwrap_or_else(|| agent_id.clone());
985
986 let federation = Federation {
987 id,
988 goal_id,
989 created_at: now,
990 members: vec![FederationMember {
991 agent_id: agent_id.clone(),
992 joined_at: now,
993 owned_goals: vec![goal_id],
994 progress: goal.progress.percentage,
995 status: MemberStatus::Active,
996 last_active: now,
997 }],
998 coordinator: Some(coordinator_id),
999 last_sync: now,
1000 sync_status: SyncStatus::Pending,
1001 collective_dreams: Vec::new(),
1002 };
1003
1004 self.federation_store.insert(id, federation.clone());
1005 self.mark_dirty();
1006 Ok(federation)
1007 }
1008
1009 pub fn join_federation(
1010 &mut self,
1011 federation_id: FederationId,
1012 agent_id: String,
1013 ) -> Result<Federation> {
1014 let now = Timestamp::now();
1015 let federation = self
1016 .federation_store
1017 .get_mut(&federation_id)
1018 .ok_or(Error::FederationNotFound(federation_id))?;
1019
1020 if let Some(member) = federation
1021 .members
1022 .iter_mut()
1023 .find(|m| m.agent_id == agent_id)
1024 {
1025 member.status = MemberStatus::Active;
1026 member.last_active = now;
1027 } else {
1028 federation.members.push(FederationMember {
1029 agent_id,
1030 joined_at: now,
1031 owned_goals: vec![federation.goal_id],
1032 progress: 0.0,
1033 status: MemberStatus::Active,
1034 last_active: now,
1035 });
1036 }
1037
1038 federation.sync_status = SyncStatus::Pending;
1039 federation.last_sync = now;
1040 let out = federation.clone();
1041 self.mark_dirty();
1042 Ok(out)
1043 }
1044
1045 pub fn sync_federation(&mut self, federation_id: FederationId) -> Result<Federation> {
1046 let now = Timestamp::now();
1047 let goal_progress = self
1048 .federation_store
1049 .get(&federation_id)
1050 .and_then(|f| self.goal_store.get(&f.goal_id))
1051 .map(|goal| goal.progress.percentage)
1052 .unwrap_or(0.0);
1053
1054 let federation = self
1055 .federation_store
1056 .get_mut(&federation_id)
1057 .ok_or(Error::FederationNotFound(federation_id))?;
1058
1059 for member in &mut federation.members {
1060 member.last_active = now;
1061 member.progress = member.progress.max(goal_progress);
1062 if matches!(
1063 member.status,
1064 MemberStatus::Blocked | MemberStatus::Inactive
1065 ) {
1066 member.status = MemberStatus::Active;
1067 }
1068 }
1069
1070 federation.last_sync = now;
1071 federation.sync_status = SyncStatus::Synced;
1072
1073 let out = federation.clone();
1074 self.mark_dirty();
1075 Ok(out)
1076 }
1077
1078 pub fn handoff_federation(
1079 &mut self,
1080 federation_id: FederationId,
1081 next_coordinator: String,
1082 ) -> Result<Federation> {
1083 let now = Timestamp::now();
1084 let federation = self
1085 .federation_store
1086 .get_mut(&federation_id)
1087 .ok_or(Error::FederationNotFound(federation_id))?;
1088
1089 if let Some(member) = federation
1090 .members
1091 .iter_mut()
1092 .find(|m| m.agent_id == next_coordinator)
1093 {
1094 member.last_active = now;
1095 member.status = MemberStatus::Active;
1096 } else {
1097 federation.members.push(FederationMember {
1098 agent_id: next_coordinator.clone(),
1099 joined_at: now,
1100 owned_goals: vec![federation.goal_id],
1101 progress: 0.0,
1102 status: MemberStatus::Active,
1103 last_active: now,
1104 });
1105 }
1106
1107 federation.coordinator = Some(next_coordinator);
1108 federation.sync_status = SyncStatus::Pending;
1109 federation.last_sync = now;
1110 let out = federation.clone();
1111 self.mark_dirty();
1112 Ok(out)
1113 }
1114
1115 pub fn detect_metamorphosis(&self, goal_id: GoalId) -> Result<MetamorphosisSignal> {
1116 let goal = self
1117 .goal_store
1118 .get(&goal_id)
1119 .ok_or(Error::GoalNotFound(goal_id))?;
1120
1121 let (should_transform, reason, change) =
1122 if goal.status == GoalStatus::Blocked && !goal.blockers.is_empty() {
1123 (
1124 true,
1125 "Goal is blocked with active blockers".to_string(),
1126 ScopeChange::Pivot {
1127 new_direction: "Remove blockers via alternate path".to_string(),
1128 reason: "Sustained blockage".to_string(),
1129 },
1130 )
1131 } else if goal.physics.momentum > 0.75 && goal.progress.percentage > 0.4 {
1132 (
1133 true,
1134 "Momentum indicates expansion opportunity".to_string(),
1135 ScopeChange::Expansion {
1136 factor: 1.25,
1137 reason: "Execution strength supports broader scope".to_string(),
1138 },
1139 )
1140 } else if goal.feelings.neglect > 0.6 && goal.progress.percentage < 0.2 {
1141 (
1142 true,
1143 "Neglect drift suggests refinement".to_string(),
1144 ScopeChange::Refinement {
1145 clarification: "Narrow scope into immediate deliverable".to_string(),
1146 },
1147 )
1148 } else {
1149 (
1150 false,
1151 "No metamorphosis signal detected".to_string(),
1152 ScopeChange::Contraction {
1153 factor: 0.95,
1154 reason: "Hold current scope".to_string(),
1155 },
1156 )
1157 };
1158
1159 Ok(MetamorphosisSignal {
1160 goal_id,
1161 should_transform,
1162 reason,
1163 recommended_change: change,
1164 })
1165 }
1166
1167 pub fn approve_metamorphosis(
1168 &mut self,
1169 goal_id: GoalId,
1170 stage_title: String,
1171 stage_description: String,
1172 change: ScopeChange,
1173 ) -> Result<Goal> {
1174 let now = Timestamp::now();
1175 let goal = self
1176 .goal_store
1177 .get_mut(&goal_id)
1178 .ok_or(Error::GoalNotFound(goal_id))?;
1179
1180 if goal.metamorphosis.is_none() {
1181 goal.metamorphosis = Some(GoalMetamorphosis {
1182 stages: Vec::new(),
1183 current_stage: 0,
1184 invariant_soul: goal.soul.clone(),
1185 });
1186 }
1187
1188 if let Some(meta) = &mut goal.metamorphosis {
1189 let stage_number = meta.stages.len() + 1;
1190 meta.stages.push(MetamorphicStage {
1191 stage_number,
1192 title: stage_title,
1193 description: stage_description,
1194 entered_at: now,
1195 scope_change: change,
1196 });
1197 meta.current_stage = meta.stages.len().saturating_sub(1);
1198 }
1199
1200 let out = goal.clone();
1201 self.mark_dirty();
1202 Ok(out)
1203 }
1204
1205 pub fn metamorphosis_history(&self, goal_id: GoalId) -> Result<Vec<MetamorphicStage>> {
1206 let goal = self
1207 .goal_store
1208 .get(&goal_id)
1209 .ok_or(Error::GoalNotFound(goal_id))?;
1210 Ok(goal
1211 .metamorphosis
1212 .as_ref()
1213 .map(|m| m.stages.clone())
1214 .unwrap_or_default())
1215 }
1216
1217 pub fn predict_metamorphosis(&self, goal_id: GoalId) -> Result<MetamorphosisPrediction> {
1218 let signal = self.detect_metamorphosis(goal_id)?;
1219 let goal = self
1220 .goal_store
1221 .get(&goal_id)
1222 .ok_or(Error::GoalNotFound(goal_id))?;
1223 let confidence =
1224 ((goal.physics.momentum + goal.feelings.urgency + goal.feelings.confidence) / 3.0)
1225 .clamp(0.1, 1.0);
1226
1227 Ok(MetamorphosisPrediction {
1228 goal_id,
1229 confidence,
1230 next_change: signal.recommended_change,
1231 rationale: signal.reason,
1232 })
1233 }
1234
1235 pub fn metamorphosis_stage(&self, goal_id: GoalId) -> Result<Option<MetamorphicStage>> {
1236 let goal = self
1237 .goal_store
1238 .get(&goal_id)
1239 .ok_or(Error::GoalNotFound(goal_id))?;
1240 let stage = goal
1241 .metamorphosis
1242 .as_ref()
1243 .and_then(|m| m.stages.get(m.current_stage).cloned());
1244 Ok(stage)
1245 }
1246
1247 pub fn merge_from(&mut self, source: &PlanningEngine) -> MergeReport {
1248 let mut report = MergeReport::default();
1249
1250 for (id, goal) in &source.goal_store {
1251 if self.goal_store.insert(*id, goal.clone()).is_none() {
1252 report.goals_merged += 1;
1253 }
1254 }
1255 for (id, decision) in &source.decision_store {
1256 if self.decision_store.insert(*id, decision.clone()).is_none() {
1257 report.decisions_merged += 1;
1258 }
1259 }
1260 for (id, commitment) in &source.commitment_store {
1261 if self
1262 .commitment_store
1263 .insert(*id, commitment.clone())
1264 .is_none()
1265 {
1266 report.commitments_merged += 1;
1267 }
1268 }
1269 for (id, dream) in &source.dream_store {
1270 if self.dream_store.insert(*id, dream.clone()).is_none() {
1271 report.dreams_merged += 1;
1272 }
1273 }
1274 for (id, federation) in &source.federation_store {
1275 if self
1276 .federation_store
1277 .insert(*id, federation.clone())
1278 .is_none()
1279 {
1280 report.federations_merged += 1;
1281 }
1282 }
1283 for (id, soul) in &source.soul_archive {
1284 if self.soul_archive.insert(*id, soul.clone()).is_none() {
1285 report.souls_merged += 1;
1286 }
1287 }
1288
1289 self.rebuild_indexes();
1290 self.mark_dirty();
1291 report
1292 }
1293
1294 pub fn update_goal(&mut self, id: GoalId, updates: UpdateGoalRequest) -> Result<Goal> {
1297 let goal = self
1298 .goal_store
1299 .get_mut(&id)
1300 .ok_or(Error::GoalNotFound(id))?;
1301
1302 if let Some(title) = updates.title {
1303 if title.is_empty() {
1304 return Err(Error::Validation("title cannot be empty".to_string()));
1305 }
1306 goal.title = title;
1307 }
1308 if let Some(description) = updates.description {
1309 goal.description = description;
1310 }
1311 if let Some(deadline) = updates.deadline {
1312 goal.deadline = deadline;
1313 }
1314 if let Some(priority) = updates.priority {
1315 let old_priority = goal.priority;
1316 goal.priority = priority;
1317 self.indexes
1318 .goal_priority_changed(id, old_priority, priority);
1319 }
1320 if let Some(tags) = updates.tags {
1321 goal.tags = tags;
1322 }
1323 if let Some(metadata) = updates.metadata {
1324 goal.metadata = metadata;
1325 }
1326 if let Some(intention) = updates.intention {
1327 goal.soul.intention = intention;
1328 }
1329 if let Some(significance) = updates.significance {
1330 goal.soul.significance = significance;
1331 }
1332 if let Some(emotional_weight) = updates.emotional_weight {
1333 goal.soul.emotional_weight = emotional_weight.clamp(0.0, 1.0);
1334 }
1335
1336 let out = goal.clone();
1337 self.mark_dirty();
1338 Ok(out)
1339 }
1340
1341 pub fn update_regret(&mut self, id: DecisionId) -> Result<Decision> {
1342 let decision = self
1343 .decision_store
1344 .get(&id)
1345 .ok_or(Error::DecisionNotFound(id))?
1346 .clone();
1347
1348 let regret = self.calculate_regret(&decision);
1349 let decision_mut = self
1350 .decision_store
1351 .get_mut(&id)
1352 .ok_or(Error::DecisionNotFound(id))?;
1353 decision_mut.regret_score = regret;
1354 decision_mut.regret_updated_at = Some(Timestamp::now());
1355
1356 if regret > 0.7 && decision_mut.status == DecisionStatus::Crystallized {
1357 decision_mut.status = DecisionStatus::Regretted;
1358 }
1359
1360 let out = decision_mut.clone();
1361 self.mark_dirty();
1362 Ok(out)
1363 }
1364
1365 pub fn record_insight(&mut self, dream_id: DreamId, insight: DreamInsight) -> Result<Dream> {
1366 let dream = self
1367 .dream_store
1368 .get_mut(&dream_id)
1369 .ok_or(Error::DreamNotFound(dream_id))?;
1370 dream.insights.push(insight);
1371 let out = dream.clone();
1372 self.mark_dirty();
1373 Ok(out)
1374 }
1375
1376 pub fn assess_accuracy(&mut self, dream_id: DreamId, accuracy: DreamAccuracy) -> Result<Dream> {
1377 let dream = self
1378 .dream_store
1379 .get_mut(&dream_id)
1380 .ok_or(Error::DreamNotFound(dream_id))?;
1381 dream.accuracy = Some(accuracy);
1382 let out = dream.clone();
1383 self.mark_dirty();
1384 Ok(out)
1385 }
1386
1387 pub fn update_momentum(&mut self, id: GoalId) -> Result<Goal> {
1388 let momentum = {
1389 let goal = self.goal_store.get(&id).ok_or(Error::GoalNotFound(id))?;
1390 self.calculate_momentum_from_goal(goal)
1391 };
1392 let goal = self
1393 .goal_store
1394 .get_mut(&id)
1395 .ok_or(Error::GoalNotFound(id))?;
1396 goal.physics.momentum = momentum;
1397 goal.physics.last_calculated = Timestamp::now();
1398 let out = goal.clone();
1399 self.mark_dirty();
1400 Ok(out)
1401 }
1402
1403 pub fn update_gravity(&mut self, id: GoalId) -> Result<Goal> {
1404 let goal = self
1405 .goal_store
1406 .get_mut(&id)
1407 .ok_or(Error::GoalNotFound(id))?;
1408
1409 let now = Timestamp::now();
1410 let deadline_pull = goal
1411 .deadline
1412 .map(|d| {
1413 let days_until = ((d.0 - now.0) as f64 / (86_400.0 * 1e9)).max(0.01);
1414 (1.0 / days_until).clamp(0.0, 1.0)
1415 })
1416 .unwrap_or(0.0);
1417
1418 let priority_pull = match goal.priority {
1419 Priority::Critical => 0.9,
1420 Priority::High => 0.7,
1421 Priority::Medium => 0.5,
1422 Priority::Low => 0.3,
1423 Priority::Someday => 0.1,
1424 };
1425
1426 let dependent_pull = (goal.dependents.len() as f64 * 0.1).clamp(0.0, 0.5);
1427 goal.physics.gravity =
1428 (deadline_pull * 0.4 + priority_pull * 0.35 + dependent_pull * 0.25).clamp(0.0, 1.0);
1429 goal.physics.last_calculated = now;
1430
1431 let out = goal.clone();
1432 self.mark_dirty();
1433 Ok(out)
1434 }
1435
1436 pub fn update_feelings(&mut self, id: GoalId) -> Result<Goal> {
1437 let now = Timestamp::now();
1438 let (momentum, confidence) = {
1439 let goal = self.goal_store.get(&id).ok_or(Error::GoalNotFound(id))?;
1440 (
1441 self.calculate_momentum_from_goal(goal),
1442 self.calculate_confidence_from_goal(goal),
1443 )
1444 };
1445
1446 let goal = self
1447 .goal_store
1448 .get_mut(&id)
1449 .ok_or(Error::GoalNotFound(id))?;
1450
1451 let deadline_urgency = goal
1453 .deadline
1454 .map(|d| {
1455 let days_until = ((d.0 - now.0) as f64 / (86_400.0 * 1e9)).max(0.01);
1456 (7.0 / days_until).clamp(0.0, 1.0)
1457 })
1458 .unwrap_or(0.2);
1459 let priority_urgency = match goal.priority {
1460 Priority::Critical => 0.9,
1461 Priority::High => 0.7,
1462 Priority::Medium => 0.4,
1463 Priority::Low => 0.2,
1464 Priority::Someday => 0.05,
1465 };
1466 goal.feelings.urgency = (deadline_urgency * 0.6 + priority_urgency * 0.4).clamp(0.0, 1.0);
1467
1468 let last_progress_days = goal
1470 .progress
1471 .history
1472 .last()
1473 .map(|p| ((now.0 - p.timestamp.0) as f64 / (86_400.0 * 1e9)).max(0.0))
1474 .unwrap_or(30.0);
1475 goal.feelings.neglect = (last_progress_days / 14.0).clamp(0.0, 1.0);
1476
1477 goal.feelings.confidence = confidence;
1479
1480 let criteria_met = goal
1482 .soul
1483 .success_criteria
1484 .iter()
1485 .filter(|c| c.achieved)
1486 .count() as f64;
1487 let criteria_total = goal.soul.success_criteria.len().max(1) as f64;
1488 goal.feelings.alignment =
1489 (criteria_met / criteria_total * 0.5 + goal.progress.percentage * 0.5).clamp(0.0, 1.0);
1490
1491 goal.feelings.vitality =
1493 (momentum * 0.6 + (1.0 - goal.feelings.neglect) * 0.4).clamp(0.0, 1.0);
1494
1495 goal.feelings.last_calculated = now;
1496 let out = goal.clone();
1497 self.mark_dirty();
1498 Ok(out)
1499 }
1500
1501 pub fn update_commitment(
1502 &mut self,
1503 id: CommitmentId,
1504 updates: UpdateCommitmentRequest,
1505 ) -> Result<Commitment> {
1506 let commitment = self
1507 .commitment_store
1508 .get_mut(&id)
1509 .ok_or(Error::CommitmentNotFound(id))?;
1510
1511 if let Some(promise) = updates.promise {
1512 commitment.promise = promise;
1513 }
1514 if let Some(due) = updates.due {
1515 commitment.due = due;
1516 }
1517 if let Some(goal) = updates.goal {
1518 commitment.goal = goal;
1519 }
1520
1521 let out = commitment.clone();
1522 self.mark_dirty();
1523 Ok(out)
1524 }
1525
1526 pub fn validate(&self) -> Vec<String> {
1527 let mut errors = Vec::new();
1528
1529 for (id, goal) in &self.goal_store {
1531 if goal.title.is_empty() {
1532 errors.push(format!("Goal {:?} has empty title", id));
1533 }
1534 if let Some(parent) = goal.parent {
1535 if !self.goal_store.contains_key(&parent) {
1536 errors.push(format!(
1537 "Goal {:?} references missing parent {:?}",
1538 id, parent
1539 ));
1540 }
1541 }
1542 for dep in &goal.dependencies {
1543 if !self.goal_store.contains_key(dep) {
1544 errors.push(format!("Goal {:?} depends on missing goal {:?}", id, dep));
1545 }
1546 }
1547 for child in &goal.children {
1548 if !self.goal_store.contains_key(child) {
1549 errors.push(format!(
1550 "Goal {:?} references missing child {:?}",
1551 id, child
1552 ));
1553 }
1554 }
1555 for decision_id in &goal.decisions {
1556 if !self.decision_store.contains_key(decision_id) {
1557 errors.push(format!(
1558 "Goal {:?} references missing decision {:?}",
1559 id, decision_id
1560 ));
1561 }
1562 }
1563 for commitment_id in &goal.commitments {
1564 if !self.commitment_store.contains_key(commitment_id) {
1565 errors.push(format!(
1566 "Goal {:?} references missing commitment {:?}",
1567 id, commitment_id
1568 ));
1569 }
1570 }
1571 for dream_id in &goal.dreams {
1572 if !self.dream_store.contains_key(dream_id) {
1573 errors.push(format!(
1574 "Goal {:?} references missing dream {:?}",
1575 id, dream_id
1576 ));
1577 }
1578 }
1579 if !(0.0..=1.0).contains(&goal.progress.percentage) {
1580 errors.push(format!(
1581 "Goal {:?} has invalid progress: {}",
1582 id, goal.progress.percentage
1583 ));
1584 }
1585 }
1586
1587 for (id, decision) in &self.decision_store {
1589 for goal_id in &decision.affected_goals {
1590 if !self.goal_store.contains_key(goal_id) {
1591 errors.push(format!(
1592 "Decision {:?} references missing goal {:?}",
1593 id, goal_id
1594 ));
1595 }
1596 }
1597 if let Some(caused_by) = decision.caused_by {
1598 if !self.decision_store.contains_key(&caused_by) {
1599 errors.push(format!(
1600 "Decision {:?} references missing parent decision {:?}",
1601 id, caused_by
1602 ));
1603 }
1604 }
1605 }
1606
1607 for (id, commitment) in &self.commitment_store {
1609 if let Some(goal_id) = commitment.goal {
1610 if !self.goal_store.contains_key(&goal_id) {
1611 errors.push(format!(
1612 "Commitment {:?} references missing goal {:?}",
1613 id, goal_id
1614 ));
1615 }
1616 }
1617 for ent in &commitment.entanglements {
1618 if !self.commitment_store.contains_key(&ent.with) {
1619 errors.push(format!(
1620 "Commitment {:?} entangled with missing commitment {:?}",
1621 id, ent.with
1622 ));
1623 }
1624 }
1625 }
1626
1627 for (id, dream) in &self.dream_store {
1629 if !self.goal_store.contains_key(&dream.goal_id) {
1630 errors.push(format!(
1631 "Dream {:?} references missing goal {:?}",
1632 id, dream.goal_id
1633 ));
1634 }
1635 }
1636
1637 for (id, federation) in &self.federation_store {
1639 if !self.goal_store.contains_key(&federation.goal_id) {
1640 errors.push(format!(
1641 "Federation {:?} references missing goal {:?}",
1642 id, federation.goal_id
1643 ));
1644 }
1645 }
1646
1647 errors
1648 }
1649}