Skip to main content

agentic_planning/
write_engine.rs

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        // Archive the goal's soul for potential reincarnation
242        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            // Store the break reason as a renegotiation record with accepted=false
813            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            // Assess renegotiation scope to determine acceptance and trust impact
854            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            // Count prior renegotiations — repeated renegotiation erodes trust faster
861            let prior_count = commitment.renegotiations.len();
862            let fatigue_penalty = (prior_count as f64 * 0.03).min(0.2);
863
864            // Reject if deliverable scope changed by more than 50%
865            let accepted = deliverable_change_ratio <= 0.5;
866
867            // Trust impact scales with change magnitude and renegotiation fatigue
868            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    // --- Missing write operations (SPEC-PART2) ---
1295
1296    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        // Urgency: increases with deadline proximity and priority
1452        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        // Neglect: increases the longer since last progress
1469        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        // Confidence: from blocker analysis
1478        goal.feelings.confidence = confidence;
1479
1480        // Alignment: how well progress tracks with intentions
1481        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        // Vitality: combination of momentum and inverse neglect
1492        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        // Validate goal references
1530        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        // Validate decision references
1588        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        // Validate commitment references
1608        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        // Validate dream references
1628        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        // Validate federation references
1638        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}