1pub mod audit;
2pub mod auth;
3pub mod bridges;
4pub mod cache;
5pub mod contracts;
6mod error;
7mod file_format;
8#[path = "indexes.rs"]
9mod indexes;
10mod inventions;
11pub mod isolation;
12pub mod locking;
13pub mod metrics;
14pub mod query;
15mod query_engine;
16pub mod types;
17mod validation;
18mod write_engine;
19
20pub use audit::{AuditAction, AuditEntityType, AuditEntry, AuditLog};
21pub use error::{Error, Result};
22pub use indexes::PlanIndexes;
23pub use types::*;
24pub use validation::{validators, ValidationError, ValidationResult};
25
26use std::collections::HashMap;
27use std::path::PathBuf;
28
29#[derive(Debug, Clone)]
30pub struct PlanningEngine {
31 pub(crate) path: Option<PathBuf>,
32 pub(crate) dirty: bool,
33 pub(crate) goal_store: HashMap<GoalId, Goal>,
34 pub(crate) decision_store: HashMap<DecisionId, Decision>,
35 pub(crate) commitment_store: HashMap<CommitmentId, Commitment>,
36 pub(crate) dream_store: HashMap<DreamId, Dream>,
37 pub(crate) federation_store: HashMap<FederationId, Federation>,
38 pub(crate) soul_archive: HashMap<GoalId, GoalSoulArchive>,
39 pub(crate) consensus_store: HashMap<DecisionId, DecisionConsensus>,
40 pub(crate) indexes: PlanIndexes,
41 pub(crate) audit_log: AuditLog,
42 pub(crate) write_count: u64,
43 pub(crate) session_id: uuid::Uuid,
44}
45
46impl PlanningEngine {
47 pub fn in_memory() -> Self {
48 Self {
49 path: None,
50 dirty: false,
51 goal_store: HashMap::new(),
52 decision_store: HashMap::new(),
53 commitment_store: HashMap::new(),
54 dream_store: HashMap::new(),
55 federation_store: HashMap::new(),
56 soul_archive: HashMap::new(),
57 consensus_store: HashMap::new(),
58 indexes: PlanIndexes::new(),
59 audit_log: AuditLog::new(),
60 write_count: 0,
61 session_id: uuid::Uuid::new_v4(),
62 }
63 }
64
65 pub fn goal_count(&self) -> usize {
66 self.goal_store.len()
67 }
68
69 pub fn decision_count(&self) -> usize {
70 self.decision_store.len()
71 }
72
73 pub fn commitment_count(&self) -> usize {
74 self.commitment_store.len()
75 }
76
77 pub fn session_id(&self) -> uuid::Uuid {
78 self.session_id
79 }
80
81 pub fn audit_log_mut(&mut self) -> &mut AuditLog {
82 &mut self.audit_log
83 }
84
85 pub fn goals(&self) -> impl Iterator<Item = &Goal> {
87 self.goal_store.values()
88 }
89
90 pub fn decisions(&self) -> impl Iterator<Item = &Decision> {
92 self.decision_store.values()
93 }
94
95 pub fn commitments(&self) -> impl Iterator<Item = &Commitment> {
97 self.commitment_store.values()
98 }
99
100 pub(crate) fn mark_dirty(&mut self) {
101 self.dirty = true;
102 }
103
104 #[allow(dead_code)]
105 pub(crate) fn rebuild_indexes(&mut self) {
106 self.indexes.rebuild_full(
107 &self.goal_store,
108 &self.decision_store,
109 &self.commitment_store,
110 &self.dream_store,
111 &self.federation_store,
112 );
113 }
114
115 pub(crate) fn calculate_initial_urgency(&self, request: &CreateGoalRequest) -> f64 {
116 let priority: f64 = match request.priority.unwrap_or(Priority::Medium) {
117 Priority::Critical => 1.0,
118 Priority::High => 0.8,
119 Priority::Medium => 0.5,
120 Priority::Low => 0.3,
121 Priority::Someday => 0.1,
122 };
123
124 let deadline_factor: f64 = request
125 .deadline
126 .map(|d| {
127 let days = (d.0 - Timestamp::now().0) as f64 / (86_400.0 * 1e9);
128 if days <= 1.0 {
129 1.0
130 } else if days <= 7.0 {
131 0.8
132 } else if days <= 30.0 {
133 0.6
134 } else {
135 0.3
136 }
137 })
138 .unwrap_or(0.4);
139
140 ((priority + deadline_factor) / 2.0).clamp(0.0, 1.0)
141 }
142
143 pub(crate) fn calculate_initial_gravity(&self, request: &CreateGoalRequest) -> f64 {
144 let emotional_weight = request.emotional_weight.unwrap_or(0.5);
145 let priority = match request.priority.unwrap_or(Priority::Medium) {
146 Priority::Critical => 0.95,
147 Priority::High => 0.8,
148 Priority::Medium => 0.6,
149 Priority::Low => 0.4,
150 Priority::Someday => 0.2,
151 };
152 ((emotional_weight + priority) / 2.0).clamp(0.0, 1.0)
153 }
154
155 pub(crate) fn calculate_initial_inertia(&self, request: &CreateGoalRequest) -> f64 {
156 let mut inertia = 0.35;
157 if let Some(deps) = &request.dependencies {
158 inertia += (deps.len() as f64 * 0.08).min(0.4);
159 }
160 if request.parent.is_some() {
161 inertia += 0.1;
162 }
163 inertia.clamp(0.1, 1.0)
164 }
165
166 pub(crate) fn calculate_velocity(&self, history: &[ProgressPoint]) -> f64 {
167 if history.len() < 2 {
168 return 0.0;
169 }
170
171 let first = &history[0];
172 let last = &history[history.len() - 1];
173 let dt_days = ((last.timestamp.0 - first.timestamp.0) as f64 / (86_400.0 * 1e9)).max(1.0);
174 let dp = (last.percentage - first.percentage).max(0.0);
175 (dp / dt_days).clamp(0.0, 1.0)
176 }
177
178 pub(crate) fn calculate_momentum_from_goal(&self, goal: &Goal) -> f64 {
179 let recent_factor = goal
180 .progress
181 .history
182 .last()
183 .map(|p| {
184 let age_days =
185 ((Timestamp::now().0 - p.timestamp.0) as f64 / (86_400.0 * 1e9)).max(0.0);
186 (1.0 - (age_days / 30.0)).clamp(0.0, 1.0)
187 })
188 .unwrap_or(0.0);
189 (goal.progress.velocity * 0.6 + recent_factor * 0.4).clamp(0.0, 1.0)
190 }
191
192 pub(crate) fn calculate_confidence_from_goal(&self, goal: &Goal) -> f64 {
193 let blocker_penalty: f64 = goal
194 .blockers
195 .iter()
196 .filter(|b| b.resolved_at.is_none())
197 .map(|b| b.severity)
198 .sum::<f64>()
199 .min(1.0);
200 let progress_boost = goal.progress.percentage * 0.5;
201 (0.5 + progress_boost - blocker_penalty * 0.6).clamp(0.0, 1.0)
202 }
203
204 pub(crate) fn calculate_reincarnation_potential(&self, goal: &Goal) -> f64 {
205 let progress_component = goal.progress.percentage;
206 let soul_component = goal.soul.emotional_weight;
207 ((progress_component + soul_component) / 2.0).clamp(0.0, 1.0)
208 }
209
210 #[allow(dead_code)]
211 pub(crate) fn check_success_criteria(&mut self, goal: &mut Goal) {
212 let now = Timestamp::now();
213 for c in &mut goal.soul.success_criteria {
214 if c.achieved {
215 continue; }
217
218 let met = if c.measurable {
219 match c.target {
221 Some(target) => goal.progress.percentage >= target,
222 None => goal.progress.percentage >= 1.0,
223 }
224 } else {
225 goal.progress.percentage >= 1.0
227 };
228
229 if met {
230 c.achieved = true;
231 c.achieved_at = Some(now);
232 }
233 }
234 }
235
236 pub(crate) fn release_completion_energy(&mut self, goal_id: GoalId) {
237 let dependent_ids = self
238 .goal_store
239 .get(&goal_id)
240 .map(|g| g.dependents.clone())
241 .unwrap_or_default();
242
243 for dep_id in dependent_ids {
244 if let Some(dep) = self.goal_store.get_mut(&dep_id) {
245 dep.physics.energy = (dep.physics.energy + 0.2).clamp(0.0, 2.0);
246 dep.physics.momentum = (dep.physics.momentum + 0.1).clamp(0.0, 1.0);
247 }
248 }
249 }
250
251 pub(crate) fn check_unblock(&mut self, goal_id: &GoalId) {
252 let should_unblock = self
253 .goal_store
254 .get(goal_id)
255 .map(|g| {
256 g.status == GoalStatus::Blocked
257 && g.blockers.iter().all(|b| b.resolved_at.is_some())
258 && g.dependencies.iter().all(|d| {
259 self.goal_store
260 .get(d)
261 .map(|dg| dg.status == GoalStatus::Completed)
262 .unwrap_or(false)
263 })
264 })
265 .unwrap_or(false);
266
267 if should_unblock {
268 if let Some(goal) = self.goal_store.get_mut(goal_id) {
269 goal.status = GoalStatus::Active;
270 }
271 }
272 }
273
274 pub(crate) fn calculate_resurrection_cost(
275 &self,
276 decision: &Decision,
277 shadow: &CrystalShadow,
278 ) -> f64 {
279 let base = if decision.status == DecisionStatus::Crystallized {
280 0.6
281 } else {
282 0.3
283 };
284 let complexity = (shadow.path.pros.len() + shadow.path.cons.len()) as f64 * 0.03;
285 (base + complexity).clamp(0.0, 1.0)
286 }
287
288 pub(crate) fn calculate_reversibility(&self, decision: &Decision) -> Reversibility {
289 let complexity = decision.shadows.len() as f64;
290 Reversibility {
291 is_reversible: complexity < 6.0,
292 reversal_cost: (0.2 + complexity * 0.1).clamp(0.0, 1.0),
293 reversal_window: Some(Timestamp::days_from_now(14.0)),
294 cascade_count: decision.causes.len(),
295 }
296 }
297
298 pub(crate) fn calculate_regret(&self, decision: &Decision) -> f64 {
299 let negative = decision
300 .consequences
301 .iter()
302 .filter(|c| matches!(c.impact, Impact::Negative))
303 .count() as f64;
304 let total = decision.consequences.len().max(1) as f64;
305 (negative / total).clamp(0.0, 1.0)
306 }
307
308 pub(crate) fn calculate_commitment_weight(&self, request: &CreateCommitmentRequest) -> f64 {
309 let stakeholder = request.stakeholder.importance.clamp(0.0, 1.0);
310 let complexity = (request.promise.deliverables.len() as f64 * 0.05).clamp(0.0, 0.4);
311 (0.3 + stakeholder * 0.5 + complexity).clamp(0.0, 1.0)
312 }
313
314 pub(crate) fn calculate_commitment_inertia(&self, request: &CreateCommitmentRequest) -> f64 {
315 let due_pressure: f64 = request
316 .due
317 .map(|d| {
318 let days = (d.0 - Timestamp::now().0) as f64 / (86_400.0 * 1e9);
319 if days <= 7.0 {
320 0.7
321 } else {
322 0.4
323 }
324 })
325 .unwrap_or(0.3);
326 due_pressure.clamp(0.0, 1.0)
327 }
328
329 pub(crate) fn calculate_breaking_cost(
330 &self,
331 request: &CreateCommitmentRequest,
332 ) -> BreakingCost {
333 let trust = request.stakeholder.importance.clamp(0.0, 1.0);
334 BreakingCost {
335 trust_damage: trust,
336 relationship_impact: (trust * 0.8).clamp(0.0, 1.0),
337 reputation_cost: (trust * 0.6).clamp(0.0, 1.0),
338 energy_to_break: 0.5,
339 cascading_effects: Vec::new(),
340 }
341 }
342
343 pub(crate) fn calculate_chain_bonus(&self, id: CommitmentId) -> f64 {
344 let commitment = match self.commitment_store.get(&id) {
345 Some(c) => c,
346 None => return 0.0,
347 };
348
349 let mut fulfilled_count: usize = 0;
351 let mut broken_count: usize = 0;
352
353 for entanglement in &commitment.entanglements {
354 if let Some(linked) = self.commitment_store.get(&entanglement.with) {
355 match linked.status {
356 CommitmentStatus::Fulfilled => fulfilled_count += 1,
357 CommitmentStatus::Broken => broken_count += 1,
358 _ => {}
359 }
360 }
361 }
362
363 let bonus = fulfilled_count as f64 * 0.05 - broken_count as f64 * 0.02;
364 bonus.clamp(-0.1, 0.3)
365 }
366
367 pub(crate) fn boost_commitment(&mut self, id: CommitmentId, bonus: f64) -> Result<()> {
368 let c = self
369 .commitment_store
370 .get_mut(&id)
371 .ok_or(Error::CommitmentNotFound(id))?;
372 c.weight = (c.weight + bonus).clamp(0.0, 1.0);
373 Ok(())
374 }
375
376 pub(crate) fn destabilize_commitment(&mut self, id: CommitmentId) -> Result<()> {
377 let c = self
378 .commitment_store
379 .get_mut(&id)
380 .ok_or(Error::CommitmentNotFound(id))?;
381 c.status = CommitmentStatus::AtRisk;
382 Ok(())
383 }
384
385 pub(crate) fn generate_completion_scenario(&self, goal: &Goal) -> CompletionScenario {
386 CompletionScenario {
387 vision: format!(
388 "{} has been achieved. Success criteria are met.",
389 goal.title
390 ),
391 feeling: "Clarity and momentum".to_string(),
392 world_changes: goal
393 .soul
394 .success_criteria
395 .iter()
396 .map(|c| format!("✓ {}", c.description))
397 .collect(),
398 stakeholder_reactions: HashMap::new(),
399 }
400 }
401
402 pub(crate) fn predict_obstacles(&self, goal: &Goal) -> Vec<DreamObstacle> {
403 let mut obstacles = Vec::new();
404
405 for dep_id in &goal.dependencies {
406 if let Some(dep) = self.goal_store.get(dep_id) {
407 if dep.status != GoalStatus::Completed {
408 obstacles.push(DreamObstacle {
409 description: format!("Dependency '{}' not complete", dep.title),
410 severity: 0.7,
411 timing: "Before completion".to_string(),
412 mitigation: Some(format!("Complete {} first", dep.title)),
413 });
414 }
415 }
416 }
417
418 obstacles
419 }
420
421 pub(crate) fn extract_insights(
422 &self,
423 goal: &Goal,
424 scenario: &CompletionScenario,
425 obstacles: &[DreamObstacle],
426 ) -> Vec<DreamInsight> {
427 let mut insights = vec![DreamInsight {
428 insight: format!("Primary completion signal: {}", scenario.feeling),
429 actionable: true,
430 action: Some("Prioritize highest leverage task this week".to_string()),
431 }];
432
433 if !obstacles.is_empty() {
434 insights.push(DreamInsight {
435 insight: format!("{} blockers predicted", obstacles.len()),
436 actionable: true,
437 action: Some("Resolve blockers before deep execution".to_string()),
438 });
439 }
440
441 if goal.progress.percentage < 0.2 {
442 insights.push(DreamInsight {
443 insight: "Early-stage goal: momentum building is critical".to_string(),
444 actionable: true,
445 action: Some("Ship a visible milestone in 7 days".to_string()),
446 });
447 }
448
449 insights
450 }
451
452 pub(crate) fn discover_sub_goals(
453 &self,
454 goal: &Goal,
455 obstacles: &[DreamObstacle],
456 ) -> Vec<GoalSeed> {
457 obstacles
458 .iter()
459 .take(2)
460 .enumerate()
461 .map(|(i, o)| GoalSeed {
462 title: format!("Mitigate blocker {}", i + 1),
463 description: o.description.clone(),
464 parent: goal.id,
465 reason: "Dream-derived mitigation".to_string(),
466 })
467 .collect()
468 }
469
470 pub(crate) fn calculate_dream_confidence(&self, goal: &Goal) -> f64 {
471 let blocker_penalty = goal
472 .blockers
473 .iter()
474 .filter(|b| b.resolved_at.is_none())
475 .map(|b| b.severity)
476 .sum::<f64>()
477 .min(1.0);
478 (0.8 - blocker_penalty * 0.5 + goal.progress.percentage * 0.2).clamp(0.1, 1.0)
479 }
480}