1use crate::connectome_manager::ConnectomeManager;
22use crate::models::{CorticalArea, CorticalID};
23use crate::types::{BduError, BduResult};
24use feagi_evolutionary::RuntimeGenome;
25use feagi_npu_neural::types::{Precision, QuantizationSpec};
26use parking_lot::RwLock;
27use std::sync::Arc;
28use tracing::{debug, error, info, trace, warn};
29
30fn autogen_subregion_display_name(genome_title: &str) -> String {
34 let t = genome_title.trim();
35 if t.is_empty() || t.eq_ignore_ascii_case("untitled") {
36 "Autogen Circuit".to_string()
37 } else {
38 t.to_string()
39 }
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum DevelopmentStage {
45 Initialization,
47 Corticogenesis,
49 Voxelogenesis,
51 Neurogenesis,
53 Synaptogenesis,
55 Completed,
57 Failed,
59}
60
61#[derive(Debug, Clone)]
63pub struct DevelopmentProgress {
64 pub stage: DevelopmentStage,
66 pub progress: u8,
68 pub cortical_areas_created: usize,
70 pub neurons_created: usize,
72 pub synapses_created: usize,
74 pub duration_ms: u64,
76}
77
78impl Default for DevelopmentProgress {
79 fn default() -> Self {
80 Self {
81 stage: DevelopmentStage::Initialization,
82 progress: 0,
83 cortical_areas_created: 0,
84 neurons_created: 0,
85 synapses_created: 0,
86 duration_ms: 0,
87 }
88 }
89}
90
91pub struct Neuroembryogenesis {
99 connectome_manager: Arc<RwLock<ConnectomeManager>>,
101
102 progress: Arc<RwLock<DevelopmentProgress>>,
104
105 start_time: std::time::Instant,
107}
108
109impl Neuroembryogenesis {
110 pub fn new(connectome_manager: Arc<RwLock<ConnectomeManager>>) -> Self {
112 Self {
113 connectome_manager,
114 progress: Arc::new(RwLock::new(DevelopmentProgress::default())),
115 start_time: std::time::Instant::now(),
116 }
117 }
118
119 pub fn get_progress(&self) -> DevelopmentProgress {
121 self.progress.read().clone()
122 }
123
124 fn sync_core_neuron_params(&self, cortical_idx: u32, area: &CorticalArea) -> BduResult<()> {
128 use crate::models::CorticalAreaExt;
129
130 let npu_arc = {
131 let manager = self.connectome_manager.read();
132 manager
133 .get_npu()
134 .cloned()
135 .ok_or_else(|| BduError::Internal("NPU not connected".to_string()))?
136 };
137
138 let mut npu_lock = npu_arc
139 .lock()
140 .map_err(|e| BduError::Internal(format!("Failed to lock NPU: {}", e)))?;
141
142 npu_lock.update_cortical_area_threshold_with_gradient(
143 cortical_idx,
144 area.firing_threshold(),
145 area.firing_threshold_increment_x(),
146 area.firing_threshold_increment_y(),
147 area.firing_threshold_increment_z(),
148 );
149 npu_lock.update_cortical_area_threshold_limit(cortical_idx, area.firing_threshold_limit());
150 npu_lock.update_cortical_area_leak(cortical_idx, area.leak_coefficient());
151 npu_lock.update_cortical_area_excitability(cortical_idx, area.neuron_excitability());
152 npu_lock.update_cortical_area_refractory_period(cortical_idx, area.refractory_period());
153 npu_lock.update_cortical_area_consecutive_fire_limit(
154 cortical_idx,
155 area.consecutive_fire_count() as u16,
156 );
157 npu_lock.update_cortical_area_snooze_period(cortical_idx, area.snooze_period());
158 npu_lock.update_cortical_area_mp_charge_accumulation(
159 cortical_idx,
160 area.mp_charge_accumulation(),
161 );
162
163 Ok(())
164 }
165
166 pub fn add_cortical_areas(
178 &mut self,
179 areas: Vec<CorticalArea>,
180 genome: &RuntimeGenome,
181 ) -> BduResult<(usize, usize)> {
182 info!(target: "feagi-bdu", "🧬 Incrementally adding {} cortical areas", areas.len());
183
184 let mut total_neurons = 0;
185 let mut total_synapses = 0;
186
187 for area in &areas {
189 let mut manager = self.connectome_manager.write();
190 manager.add_cortical_area(area.clone())?;
191 info!(target: "feagi-bdu", " ✓ Added cortical area structure: {}", area.cortical_id.as_base_64());
192 }
193
194 use feagi_structures::genomic::cortical_area::CoreCorticalType;
197 let death_id = CoreCorticalType::Death.to_cortical_id();
198 let power_id = CoreCorticalType::Power.to_cortical_id();
199 let fatigue_id = CoreCorticalType::Fatigue.to_cortical_id();
200 let pain_id = CoreCorticalType::Pain.to_cortical_id();
201 let pleasure_id = CoreCorticalType::Pleasure.to_cortical_id();
202 let fear_id = CoreCorticalType::Fear.to_cortical_id();
203 let hope_id = CoreCorticalType::Hope.to_cortical_id();
204
205 let mut core_areas = Vec::new();
206 let mut other_areas = Vec::new();
207
208 for area in &areas {
210 if area.cortical_id == death_id {
211 core_areas.push((0, area)); } else if area.cortical_id == power_id {
213 core_areas.push((1, area)); } else if area.cortical_id == fatigue_id {
215 core_areas.push((2, area)); } else if area.cortical_id == pain_id {
217 core_areas.push((3, area)); } else if area.cortical_id == pleasure_id {
219 core_areas.push((4, area)); } else if area.cortical_id == fear_id {
221 core_areas.push((5, area)); } else if area.cortical_id == hope_id {
223 core_areas.push((6, area)); } else {
225 other_areas.push(area);
226 }
227 }
228
229 core_areas.sort_by_key(|(idx, _)| *idx);
231
232 if !core_areas.is_empty() {
234 info!(target: "feagi-bdu", " 🎯 Creating core area neurons FIRST ({} areas) for deterministic IDs", core_areas.len());
235 for (core_idx, area) in &core_areas {
236 let existing_core_neurons = {
237 let manager = self.connectome_manager.read();
238 let npu = manager.get_npu();
239 match npu {
240 Some(npu_arc) => {
241 let npu_lock = npu_arc.lock();
242 match npu_lock {
243 Ok(npu_guard) => {
244 npu_guard.get_neurons_in_cortical_area(*core_idx).len()
245 }
246 Err(_) => 0,
247 }
248 }
249 None => 0,
250 }
251 };
252
253 if existing_core_neurons > 0 {
254 self.sync_core_neuron_params(*core_idx, area)?;
255 let refreshed = {
256 let manager = self.connectome_manager.read();
257 manager.refresh_neuron_count_for_area(&area.cortical_id)
258 };
259 let count = refreshed.unwrap_or(existing_core_neurons);
260 total_neurons += count;
261 info!(
262 target: "feagi-bdu",
263 " ↪ Skipping core neuron creation for {} (existing={}, idx={})",
264 area.cortical_id.as_base_64(),
265 count,
266 core_idx
267 );
268 continue;
269 }
270 let neurons_created = {
271 let mut manager = self.connectome_manager.write();
272 manager.create_neurons_for_area(&area.cortical_id)
273 };
274
275 match neurons_created {
276 Ok(count) => {
277 total_neurons += count as usize;
278 info!(target: "feagi-bdu", " ✅ Created {} neurons for core area {} (deterministic ID: neuron {})",
279 count, area.cortical_id.as_base_64(), core_idx);
280 }
281 Err(e) => {
282 error!(target: "feagi-bdu", " ❌ FATAL: Failed to create neurons for core area {}: {}", area.cortical_id.as_base_64(), e);
283 return Err(e);
284 }
285 }
286 }
287 }
288
289 for area in &other_areas {
291 let neurons_created = {
292 let mut manager = self.connectome_manager.write();
293 manager.create_neurons_for_area(&area.cortical_id)
294 };
295
296 match neurons_created {
297 Ok(count) => {
298 total_neurons += count as usize;
299 trace!(
300 target: "feagi-bdu",
301 "Created {} neurons for area {}",
302 count,
303 area.cortical_id.as_base_64()
304 );
305 }
306 Err(e) => {
307 error!(target: "feagi-bdu", " ❌ FATAL: Failed to create neurons for {}: {}", area.cortical_id.as_base_64(), e);
308 return Err(e);
310 }
311 }
312 }
313
314 for area in &areas {
316 let has_dstmap = area
318 .properties
319 .get("cortical_mapping_dst")
320 .and_then(|v| v.as_object())
321 .map(|m| !m.is_empty())
322 .unwrap_or(false);
323
324 if !has_dstmap {
325 debug!(target: "feagi-bdu", " No mappings for area {}", area.cortical_id.as_base_64());
326 continue;
327 }
328
329 let synapses_created = {
330 let mut manager = self.connectome_manager.write();
331 manager.apply_cortical_mapping(&area.cortical_id)
332 };
333
334 match synapses_created {
335 Ok(count) => {
336 total_synapses += count as usize;
337 trace!(
338 target: "feagi-bdu",
339 "Created {} synapses for area {}",
340 count,
341 area.cortical_id
342 );
343 }
344 Err(e) => {
345 warn!(target: "feagi-bdu", " ⚠️ Failed to create synapses for {}: {}", area.cortical_id, e);
346 let estimated = estimate_synapses_for_area(area, genome);
347 total_synapses += estimated;
348 }
349 }
350 }
351
352 info!(target: "feagi-bdu", "✅ Incremental add complete: {} areas, {} neurons, {} synapses",
353 areas.len(), total_neurons, total_synapses);
354
355 Ok((total_neurons, total_synapses))
356 }
357
358 pub fn develop_from_genome(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
362 info!(target: "feagi-bdu","🧬 Starting neuroembryogenesis for genome: {}", genome.metadata.genome_id);
363
364 let _quantization_precision = &genome.physiology.quantization_precision;
366 let quant_spec = QuantizationSpec::default();
368
369 info!(target: "feagi-bdu",
370 " Quantization precision: {:?} (range: [{}, {}] for membrane potential)",
371 quant_spec.precision,
372 quant_spec.membrane_potential_min,
373 quant_spec.membrane_potential_max
374 );
375
376 match quant_spec.precision {
380 Precision::FP32 => {
381 info!(target: "feagi-bdu", " ✓ Using FP32 (32-bit floating-point) - highest precision");
382 info!(target: "feagi-bdu", " Memory usage: Baseline (4 bytes/neuron for membrane potential)");
383 }
384 Precision::INT8 => {
385 info!(target: "feagi-bdu", " ✓ Using INT8 (8-bit integer) - memory efficient");
386 info!(target: "feagi-bdu", " Memory reduction: 42% (1 byte/neuron for membrane potential)");
387 info!(target: "feagi-bdu", " Quantization range: [{}, {}]",
388 quant_spec.membrane_potential_min,
389 quant_spec.membrane_potential_max);
390 }
393 Precision::FP16 => {
394 warn!(target: "feagi-bdu", " FP16 quantization requested but not yet implemented.");
395 warn!(target: "feagi-bdu", " FP16 support planned for future GPU optimization.");
396 }
398 }
399
400 info!(target: "feagi-bdu", " ✓ Quantization handled by DynamicNPU (dispatches at runtime)");
403
404 self.update_stage(DevelopmentStage::Initialization, 0);
406
407 self.corticogenesis(genome)?;
409
410 self.voxelogenesis(genome)?;
412
413 self.neurogenesis(genome)?;
415
416 self.synaptogenesis(genome)?;
418
419 self.update_stage(DevelopmentStage::Completed, 100);
421
422 let progress = self.progress.read();
423 info!(target: "feagi-bdu",
424 "✅ Neuroembryogenesis completed in {}ms: {} cortical areas, {} neurons, {} synapses",
425 progress.duration_ms,
426 progress.cortical_areas_created,
427 progress.neurons_created,
428 progress.synapses_created
429 );
430
431 Ok(())
432 }
433
434 fn corticogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
436 self.update_stage(DevelopmentStage::Corticogenesis, 0);
437 info!(target: "feagi-bdu","🧠 Stage 1: Corticogenesis - Creating {} cortical areas", genome.cortical_areas.len());
438 info!(target: "feagi-bdu","🔍 Genome brain_regions check: is_empty={}, count={}",
439 genome.brain_regions.is_empty(), genome.brain_regions.len());
440 if !genome.brain_regions.is_empty() {
441 info!(target: "feagi-bdu"," Existing regions: {:?}", genome.brain_regions.keys().collect::<Vec<_>>());
442 }
443
444 let total_areas = genome.cortical_areas.len();
445
446 for (idx, (cortical_id, area)) in genome.cortical_areas.iter().enumerate() {
448 {
450 let mut manager = self.connectome_manager.write();
451 manager.add_cortical_area(area.clone())?;
452 } let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
456 self.update_progress(|p| {
457 p.cortical_areas_created = idx + 1;
458 p.progress = progress_pct;
459 });
460
461 trace!(target: "feagi-bdu", "Created cortical area: {} ({})", cortical_id, area.name);
462 }
463
464 info!(target: "feagi-bdu","🔍 BRAIN REGION AUTO-GEN CHECK: genome.brain_regions.is_empty() = {}", genome.brain_regions.is_empty());
467 let (brain_regions_to_add, region_parent_map) = if genome.brain_regions.is_empty() {
468 info!(target: "feagi-bdu"," ✅ TRIGGERING AUTO-GENERATION: No brain_regions in genome - auto-generating default root region");
469 info!(target: "feagi-bdu"," 📊 Genome has {} cortical areas to process", genome.cortical_areas.len());
470
471 let all_cortical_ids = genome.cortical_areas.keys().cloned().collect::<Vec<_>>();
473 info!(target: "feagi-bdu"," 📊 Collected {} cortical area IDs: {:?}", all_cortical_ids.len(),
474 if all_cortical_ids.len() <= 5 {
475 format!("{:?}", all_cortical_ids.iter().map(|id| id.to_string()).collect::<Vec<_>>())
476 } else {
477 format!("{:?}...", all_cortical_ids[0..5].iter().map(|id| id.to_string()).collect::<Vec<_>>())
478 });
479
480 let mut auto_inputs = Vec::new();
482 let mut auto_outputs = Vec::new();
483
484 let mut ipu_areas = Vec::new(); let mut opu_areas = Vec::new(); let mut core_areas = Vec::new(); let mut custom_memory_areas = Vec::new(); for (area_id, area) in genome.cortical_areas.iter() {
491 let area_id_str = area_id.to_string();
498 let category = if area_id_str.starts_with("___") {
500 "CORE"
501 } else if let Ok(cortical_type) = area.cortical_id.as_cortical_type() {
502 use feagi_structures::genomic::cortical_area::CorticalAreaType;
504 match cortical_type {
505 CorticalAreaType::Core(_) => "CORE",
506 CorticalAreaType::BrainInput(_) => "IPU",
507 CorticalAreaType::BrainOutput(_) => "OPU",
508 CorticalAreaType::Memory(_) => "MEMORY",
509 CorticalAreaType::Custom(_) => "CUSTOM",
510 }
511 } else {
512 let cortical_group = area
514 .properties
515 .get("cortical_group")
516 .and_then(|v| v.as_str())
517 .map(|s| s.to_uppercase());
518
519 match cortical_group.as_deref() {
520 Some("IPU") => "IPU",
521 Some("OPU") => "OPU",
522 Some("CORE") => "CORE",
523 Some("MEMORY") => "MEMORY",
524 Some("CUSTOM") => "CUSTOM",
525 _ => "CUSTOM", }
527 };
528
529 if ipu_areas.len() + opu_areas.len() + core_areas.len() + custom_memory_areas.len()
531 < 5
532 {
533 let source = if area.cortical_id.as_cortical_type().is_ok() {
534 "cortical_id_type"
535 } else if area.properties.contains_key("cortical_group") {
536 "cortical_group"
537 } else {
538 "default_fallback"
539 };
540
541 if area.cortical_id.as_cortical_type().is_ok() {
543 let type_desc = crate::cortical_type_utils::describe_cortical_type(area);
544 let frame_handling =
545 if crate::cortical_type_utils::uses_absolute_frames(area) {
546 "absolute"
547 } else if crate::cortical_type_utils::uses_incremental_frames(area) {
548 "incremental"
549 } else {
550 "n/a"
551 };
552 info!(target: "feagi-bdu"," 🔍 {}, frames={}, source={}",
553 type_desc, frame_handling, source);
554 } else {
555 info!(target: "feagi-bdu"," 🔍 Area {}: category={}, source={}",
556 area_id_str, category, source);
557 }
558 }
559
560 match category {
562 "IPU" => {
563 ipu_areas.push(*area_id);
564 auto_inputs.push(*area_id);
565 }
566 "OPU" => {
567 opu_areas.push(*area_id);
568 auto_outputs.push(*area_id);
569 }
570 "CORE" => {
571 core_areas.push(*area_id);
572 }
573 "MEMORY" | "CUSTOM" => {
574 custom_memory_areas.push(*area_id);
575 }
576 _ => {}
577 }
578 }
579
580 info!(target: "feagi-bdu"," 📊 Classification complete: IPU={}, OPU={}, CORE={}, CUSTOM/MEMORY={}",
581 ipu_areas.len(), opu_areas.len(), core_areas.len(), custom_memory_areas.len());
582
583 use feagi_structures::genomic::brain_regions::{BrainRegion, RegionID, RegionType};
585 let mut regions_map = std::collections::HashMap::new();
586
587 let mut root_area_ids = Vec::new();
589 root_area_ids.extend(ipu_areas.iter().cloned());
590 root_area_ids.extend(opu_areas.iter().cloned());
591 root_area_ids.extend(core_areas.iter().cloned());
592
593 let (root_inputs, root_outputs) =
595 Self::analyze_region_io(&root_area_ids, &genome.cortical_areas);
596
597 let root_region_id = RegionID::new();
600 let root_region_id_str = root_region_id.to_string();
601
602 let mut root_region = BrainRegion::new(
603 root_region_id,
604 "Root Brain Region".to_string(),
605 RegionType::Undefined,
606 )
607 .expect("Failed to create root region")
608 .with_areas(root_area_ids.iter().cloned());
609
610 if !root_inputs.is_empty() {
612 root_region
613 .add_property("inputs".to_string(), serde_json::json!(root_inputs.clone()));
614 }
615 if !root_outputs.is_empty() {
616 root_region.add_property(
617 "outputs".to_string(),
618 serde_json::json!(root_outputs.clone()),
619 );
620 }
621
622 info!(target: "feagi-bdu"," ✅ Created root region with {} areas (IPU={}, OPU={}, CORE={}) - analyzed: {} inputs, {} outputs",
623 root_area_ids.len(), ipu_areas.len(), opu_areas.len(), core_areas.len(),
624 root_inputs.len(), root_outputs.len());
625
626 let mut subregion_id = None;
628 if !custom_memory_areas.is_empty() {
629 let mut custom_memory_strs: Vec<String> = custom_memory_areas
631 .iter()
632 .map(|id| id.as_base_64())
633 .collect();
634 custom_memory_strs.sort(); let combined = custom_memory_strs.join("|");
636
637 use std::collections::hash_map::DefaultHasher;
639 use std::hash::{Hash, Hasher};
640 let mut hasher = DefaultHasher::new();
641 combined.hash(&mut hasher);
642 let hash = hasher.finish();
643 let hash_hex = format!("{:08x}", hash as u32);
644 let region_id = format!("region_autogen_{}", hash_hex);
645
646 let (subregion_inputs, subregion_outputs) =
648 Self::analyze_region_io(&custom_memory_areas, &genome.cortical_areas);
649
650 let autogen_position =
652 Self::calculate_autogen_region_position(&root_area_ids, genome);
653
654 let subregion_name = autogen_subregion_display_name(&genome.metadata.genome_title);
656 let mut subregion = BrainRegion::new(
657 RegionID::new(), subregion_name,
659 RegionType::Undefined, )
661 .expect("Failed to create subregion")
662 .with_areas(custom_memory_areas.iter().cloned());
663
664 subregion.add_property(
666 "coordinate_3d".to_string(),
667 serde_json::json!(autogen_position),
668 );
669 subregion.add_property("coordinate_2d".to_string(), serde_json::json!([0, 0]));
670
671 if !subregion_inputs.is_empty() {
673 subregion.add_property(
674 "inputs".to_string(),
675 serde_json::json!(subregion_inputs.clone()),
676 );
677 }
678 if !subregion_outputs.is_empty() {
679 subregion.add_property(
680 "outputs".to_string(),
681 serde_json::json!(subregion_outputs.clone()),
682 );
683 }
684
685 let subregion_id_str = subregion.region_id.to_string();
686
687 info!(target: "feagi-bdu"," ✅ Created subregion '{}' with {} CUSTOM/MEMORY areas ({} inputs, {} outputs)",
688 region_id, custom_memory_areas.len(), subregion_inputs.len(), subregion_outputs.len());
689
690 regions_map.insert(subregion_id_str.clone(), subregion);
691 subregion_id = Some(subregion_id_str);
692 }
693
694 regions_map.insert(root_region_id_str.clone(), root_region);
695
696 let total_inputs = root_inputs.len()
698 + if let Some(ref sid) = subregion_id {
699 regions_map
700 .get(sid)
701 .and_then(|r| r.properties.get("inputs"))
702 .and_then(|v| v.as_array())
703 .map(|a| a.len())
704 .unwrap_or(0)
705 } else {
706 0
707 };
708
709 let total_outputs = root_outputs.len()
710 + if let Some(ref sid) = subregion_id {
711 regions_map
712 .get(sid)
713 .and_then(|r| r.properties.get("outputs"))
714 .and_then(|v| v.as_array())
715 .map(|a| a.len())
716 .unwrap_or(0)
717 } else {
718 0
719 };
720
721 info!(target: "feagi-bdu"," ✅ Auto-generated {} brain region(s) with {} total cortical areas ({} total inputs, {} total outputs)",
722 regions_map.len(), all_cortical_ids.len(), total_inputs, total_outputs);
723
724 let mut parent_map = std::collections::HashMap::new();
726 if let Some(ref sub_id) = subregion_id {
727 parent_map.insert(sub_id.clone(), root_region_id_str.clone());
728 info!(target: "feagi-bdu"," 🔗 Parent relationship: {} -> {}", sub_id, root_region_id_str);
729 }
730
731 (regions_map, parent_map)
732 } else {
733 info!(target: "feagi-bdu"," 📋 Genome already has {} brain regions - using existing structure", genome.brain_regions.len());
734 let mut region_parent_map: std::collections::HashMap<String, String> =
738 std::collections::HashMap::new();
739 for (region_id, region) in &genome.brain_regions {
740 if let Some(pid) = region
741 .properties
742 .get("parent_region_id")
743 .and_then(|v| v.as_str())
744 {
745 region_parent_map.insert(region_id.clone(), pid.to_string());
746 }
747 }
748 if region_parent_map.is_empty() {
749 if let Some((root_id, _)) = genome
750 .brain_regions
751 .iter()
752 .find(|(_, r)| r.name == "Root Brain Region")
753 {
754 for (region_id, region) in &genome.brain_regions {
755 if region.name == "Root Brain Region" {
756 continue;
757 }
758 region_parent_map.insert(region_id.clone(), root_id.clone());
759 }
760 if !region_parent_map.is_empty() {
761 info!(target: "feagi-bdu",
762 " 🔗 Inferred {} sub-region parent link(s) under root {}",
763 region_parent_map.len(),
764 root_id
765 );
766 }
767 } else {
768 warn!(target: "feagi-bdu",
769 " ⚠️ brain_regions present but no 'Root Brain Region' and no parent_region_id — hierarchy may not load in BV"
770 );
771 }
772 }
773 (genome.brain_regions.clone(), region_parent_map)
774 };
775
776 {
778 let mut manager = self.connectome_manager.write();
779 let brain_region_count = brain_regions_to_add.len();
780 info!(target: "feagi-bdu"," Adding {} brain regions from genome", brain_region_count);
781
782 let root_entry = brain_regions_to_add
784 .iter()
785 .find(|(_, region)| region.name == "Root Brain Region");
786 if let Some((root_id, root_region)) = root_entry {
787 manager.add_brain_region(root_region.clone(), None)?;
788 debug!(target: "feagi-bdu"," ✓ Added brain region: {} (Root Brain Region) [parent=None]", root_id);
789 }
790
791 for (region_id, region) in brain_regions_to_add.iter() {
793 if region.name == "Root Brain Region" {
794 continue; }
796
797 let parent_id = region_parent_map.get(region_id).cloned();
798 manager.add_brain_region(region.clone(), parent_id.clone())?;
799 debug!(target: "feagi-bdu"," ✓ Added brain region: {} ({}) [parent={:?}]",
800 region_id, region.name, parent_id);
801 }
802
803 info!(target: "feagi-bdu"," Total brain regions in ConnectomeManager: {}", manager.get_brain_region_ids().len());
804 } self.update_stage(DevelopmentStage::Corticogenesis, 100);
807 info!(target: "feagi-bdu"," ✅ Corticogenesis complete: {} cortical areas created", total_areas);
808
809 Ok(())
810 }
811
812 fn voxelogenesis(&mut self, _genome: &RuntimeGenome) -> BduResult<()> {
814 self.update_stage(DevelopmentStage::Voxelogenesis, 0);
815 info!(target: "feagi-bdu","📐 Stage 2: Voxelogenesis - Establishing spatial framework");
816
817 self.update_stage(DevelopmentStage::Voxelogenesis, 100);
821 info!(target: "feagi-bdu"," ✅ Voxelogenesis complete: Spatial framework established");
822
823 Ok(())
824 }
825
826 fn neurogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
835 self.update_stage(DevelopmentStage::Neurogenesis, 0);
836 info!(target: "feagi-bdu","🔬 Stage 3: Neurogenesis - Generating neurons (SIMD-optimized batches)");
837
838 let expected_neurons = genome.stats.innate_neuron_count;
839 info!(target: "feagi-bdu"," Expected innate neurons from genome: {}", expected_neurons);
840
841 use feagi_structures::genomic::cortical_area::CoreCorticalType;
843 let death_id = CoreCorticalType::Death.to_cortical_id();
844 let power_id = CoreCorticalType::Power.to_cortical_id();
845 let fatigue_id = CoreCorticalType::Fatigue.to_cortical_id();
846 let pain_id = CoreCorticalType::Pain.to_cortical_id();
847 let pleasure_id = CoreCorticalType::Pleasure.to_cortical_id();
848 let fear_id = CoreCorticalType::Fear.to_cortical_id();
849 let hope_id = CoreCorticalType::Hope.to_cortical_id();
850
851 let mut core_areas = Vec::new();
852 let mut other_areas = Vec::new();
853
854 for (cortical_id, area) in genome.cortical_areas.iter() {
856 if *cortical_id == death_id {
857 core_areas.push((0, *cortical_id, area)); } else if *cortical_id == power_id {
859 core_areas.push((1, *cortical_id, area)); } else if *cortical_id == fatigue_id {
861 core_areas.push((2, *cortical_id, area)); } else if *cortical_id == pain_id {
863 core_areas.push((3, *cortical_id, area)); } else if *cortical_id == pleasure_id {
865 core_areas.push((4, *cortical_id, area)); } else if *cortical_id == fear_id {
867 core_areas.push((5, *cortical_id, area)); } else if *cortical_id == hope_id {
869 core_areas.push((6, *cortical_id, area)); } else {
871 other_areas.push((*cortical_id, area));
872 }
873 }
874
875 core_areas.sort_by_key(|(idx, _, _)| *idx);
877
878 info!(target: "feagi-bdu"," 🎯 Creating core area neurons FIRST ({} areas) for deterministic IDs", core_areas.len());
879
880 let mut total_neurons_created = 0;
881 let mut processed_count = 0;
882 let total_areas = genome.cortical_areas.len();
883
884 for (core_idx, cortical_id, area) in &core_areas {
886 let existing_core_neurons = {
887 let manager = self.connectome_manager.read();
888 let npu = manager.get_npu();
889 match npu {
890 Some(npu_arc) => {
891 let npu_lock = npu_arc.lock();
892 match npu_lock {
893 Ok(npu_guard) => {
894 npu_guard.get_neurons_in_cortical_area(*core_idx).len()
895 }
896 Err(_) => 0,
897 }
898 }
899 None => 0,
900 }
901 };
902
903 if existing_core_neurons > 0 {
904 self.sync_core_neuron_params(*core_idx, area)?;
905 let refreshed = {
906 let manager = self.connectome_manager.read();
907 manager.refresh_neuron_count_for_area(cortical_id)
908 };
909 let count = refreshed.unwrap_or(existing_core_neurons);
910 total_neurons_created += count;
911 info!(
912 target: "feagi-bdu",
913 " ↪ Skipping core neuron creation for {} (existing={}, idx={})",
914 cortical_id.as_base_64(),
915 count,
916 core_idx
917 );
918 processed_count += 1;
919 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
920 self.update_progress(|p| {
921 p.neurons_created = total_neurons_created;
922 p.progress = progress_pct;
923 });
924 continue;
925 }
926 let per_voxel_count = area
927 .properties
928 .get("neurons_per_voxel")
929 .and_then(|v| v.as_u64())
930 .unwrap_or(1) as i64;
931
932 let cortical_id_str = cortical_id.to_string();
933 info!(target: "feagi-bdu"," 🔋 [CORE-AREA {}] {} - dimensions: {:?}, per_voxel: {}",
934 core_idx, cortical_id_str, area.dimensions, per_voxel_count);
935
936 if per_voxel_count == 0 {
937 warn!(target: "feagi-bdu"," ⚠️ Skipping core area {} - per_voxel_neuron_cnt is 0", cortical_id_str);
938 continue;
939 }
940
941 let neurons_created = {
943 let manager_arc = self.connectome_manager.clone();
944 let mut manager = manager_arc.write();
945 manager.create_neurons_for_area(cortical_id)
946 };
947
948 match neurons_created {
949 Ok(count) => {
950 total_neurons_created += count as usize;
951 info!(target: "feagi-bdu"," ✅ Created {} neurons for core area {} (deterministic ID: neuron {})",
952 count, cortical_id_str, core_idx);
953 }
954 Err(e) => {
955 error!(target: "feagi-bdu"," ❌ FATAL: Failed to create neurons for core area {}: {}", cortical_id_str, e);
956 return Err(e);
957 }
958 }
959
960 processed_count += 1;
961 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
962 self.update_progress(|p| {
963 p.neurons_created = total_neurons_created;
964 p.progress = progress_pct;
965 });
966 }
967
968 info!(target: "feagi-bdu"," 📦 Creating neurons for {} other areas", other_areas.len());
970 for (cortical_id, area) in &other_areas {
971 let _per_voxel_count = area
973 .properties
974 .get("neurons_per_voxel")
975 .and_then(|v| v.as_u64())
976 .unwrap_or(1) as i64;
977
978 let per_voxel_count = area
979 .properties
980 .get("neurons_per_voxel")
981 .and_then(|v| v.as_u64())
982 .unwrap_or(1) as i64;
983
984 let cortical_id_str = cortical_id.to_string();
985
986 if per_voxel_count == 0 {
987 warn!(target: "feagi-bdu"," ⚠️ Skipping area {} - per_voxel_neuron_cnt is 0 (will have NO neurons!)", cortical_id_str);
988 continue;
989 }
990
991 let neurons_created = {
994 let manager_arc = self.connectome_manager.clone();
995 let mut manager = manager_arc.write();
996 manager.create_neurons_for_area(cortical_id)
997 }; match neurons_created {
1000 Ok(count) => {
1001 total_neurons_created += count as usize;
1002 trace!(
1003 target: "feagi-bdu",
1004 "Created {} neurons for area {}",
1005 count,
1006 cortical_id_str
1007 );
1008 }
1009 Err(e) => {
1010 warn!(target: "feagi-bdu"," Failed to create neurons for {}: {} (NPU may not be connected)",
1012 cortical_id_str, e);
1013 let total_voxels = area.dimensions.width as usize
1014 * area.dimensions.height as usize
1015 * area.dimensions.depth as usize;
1016 let expected = total_voxels * per_voxel_count as usize;
1017 total_neurons_created += expected;
1018 }
1019 }
1020
1021 processed_count += 1;
1022 let progress_pct = (processed_count * 100 / total_areas.max(1)) as u8;
1024 self.update_progress(|p| {
1025 p.neurons_created = total_neurons_created;
1026 p.progress = progress_pct;
1027 });
1028 }
1029
1030 if expected_neurons > 0 && total_neurons_created != expected_neurons {
1032 trace!(target: "feagi-bdu",
1033 created_neurons = total_neurons_created,
1034 genome_stats_innate = expected_neurons,
1035 "Neuron creation complete (genome stats may only count innate neurons)"
1036 );
1037 }
1038
1039 self.update_stage(DevelopmentStage::Neurogenesis, 100);
1040 info!(target: "feagi-bdu"," ✅ Neurogenesis complete: {} neurons created", total_neurons_created);
1041
1042 Ok(())
1043 }
1044
1045 fn synaptogenesis(&mut self, genome: &RuntimeGenome) -> BduResult<()> {
1051 self.update_stage(DevelopmentStage::Synaptogenesis, 0);
1052 info!(target: "feagi-bdu","🔗 Stage 4: Synaptogenesis - Forming synaptic connections (SIMD-optimized batches)");
1053
1054 let expected_synapses = genome.stats.innate_synapse_count;
1055 info!(target: "feagi-bdu"," Expected innate synapses from genome: {}", expected_synapses);
1056
1057 self.rebuild_memory_twin_mappings_from_genome(genome)?;
1058
1059 let mut total_synapses_created = 0;
1060 let total_areas = genome.cortical_areas.len();
1061
1062 for (idx, (_src_cortical_id, src_area)) in genome.cortical_areas.iter().enumerate() {
1065 let has_dstmap = src_area
1067 .properties
1068 .get("cortical_mapping_dst")
1069 .and_then(|v| v.as_object())
1070 .map(|m| !m.is_empty())
1071 .unwrap_or(false);
1072
1073 if !has_dstmap {
1074 trace!(target: "feagi-bdu", "No dstmap for area {}", &src_area.cortical_id);
1075 continue;
1076 }
1077
1078 let src_cortical_id = &src_area.cortical_id;
1082 let src_cortical_id_str = src_cortical_id.to_string(); let synapses_created = {
1084 let manager_arc = self.connectome_manager.clone();
1085 let mut manager = manager_arc.write();
1086 if let Some(dstmap) = src_area.properties.get("cortical_mapping_dst") {
1087 if let Some(area) = manager.get_cortical_area_mut(src_cortical_id) {
1088 area.properties
1089 .insert("cortical_mapping_dst".to_string(), dstmap.clone());
1090 }
1091 }
1092 manager.apply_cortical_mapping(src_cortical_id)
1093 }; match synapses_created {
1096 Ok(count) => {
1097 total_synapses_created += count as usize;
1098 trace!(
1099 target: "feagi-bdu",
1100 "Created {} synapses for area {}",
1101 count,
1102 src_cortical_id_str
1103 );
1104 }
1105 Err(e) => {
1106 warn!(target: "feagi-bdu"," Failed to create synapses for {}: {} (NPU may not be connected)",
1108 src_cortical_id_str, e);
1109 let estimated = estimate_synapses_for_area(src_area, genome);
1110 total_synapses_created += estimated;
1111 }
1112 }
1113
1114 let progress_pct = ((idx + 1) * 100 / total_areas.max(1)) as u8;
1116 self.update_progress(|p| {
1117 p.synapses_created = total_synapses_created;
1118 p.progress = progress_pct;
1119 });
1120 }
1121
1122 let npu_arc = {
1127 let manager = self.connectome_manager.read();
1128 manager.get_npu().cloned()
1129 };
1130 if let Some(npu_arc) = npu_arc {
1131 let mut npu_lock = npu_arc
1132 .lock()
1133 .map_err(|e| BduError::Internal(format!("Failed to lock NPU: {}", e)))?;
1134 npu_lock.rebuild_synapse_index();
1135
1136 let manager = self.connectome_manager.read();
1138 manager.update_cached_synapse_count();
1139 }
1140
1141 #[cfg(feature = "plasticity")]
1144 {
1145 use feagi_evolutionary::extract_memory_properties;
1146 use feagi_npu_plasticity::{MemoryNeuronLifecycleConfig, PlasticityExecutor};
1147
1148 let manager = self.connectome_manager.read();
1149 if let Some(executor) = manager.get_plasticity_executor() {
1150 let mut registered_count = 0;
1151
1152 for area_id in manager.get_cortical_area_ids() {
1154 if let Some(area) = manager.get_cortical_area(area_id) {
1155 if let Some(mem_props) = extract_memory_properties(&area.properties) {
1156 let upstream_areas = manager.get_upstream_cortical_areas(area_id);
1157
1158 if let Some(npu_arc) = manager.get_npu() {
1161 if let Ok(mut npu) = npu_arc.lock() {
1162 let existing_configs = npu.get_all_fire_ledger_configs();
1163 for &upstream_idx in &upstream_areas {
1164 let existing = existing_configs
1165 .iter()
1166 .find(|(idx, _)| *idx == upstream_idx)
1167 .map(|(_, w)| *w)
1168 .unwrap_or(0);
1169
1170 let desired = mem_props.temporal_depth as usize;
1171 let resolved = existing.max(desired);
1172 if resolved != existing {
1173 if let Err(e) = npu.configure_fire_ledger_window(
1174 upstream_idx,
1175 resolved,
1176 ) {
1177 warn!(
1178 target: "feagi-bdu",
1179 "Failed to configure FireLedger window for upstream area idx={} (requested={}): {}",
1180 upstream_idx,
1181 resolved,
1182 e
1183 );
1184 }
1185 }
1186 }
1187 } else {
1188 warn!(target: "feagi-bdu", "Failed to lock NPU for FireLedger configuration");
1189 }
1190 }
1191
1192 if let Ok(exec) = executor.lock() {
1193 let upstream_non_memory =
1194 manager.filter_non_memory_upstream_areas(&upstream_areas);
1195 let lifecycle_config = MemoryNeuronLifecycleConfig {
1196 initial_lifespan: mem_props.init_lifespan,
1197 lifespan_growth_rate: mem_props.lifespan_growth_rate,
1198 longterm_threshold: mem_props.longterm_threshold,
1199 max_reactivations: 1000,
1200 };
1201
1202 exec.register_memory_area(
1203 area.cortical_idx,
1204 area_id.as_base_64(),
1205 mem_props.temporal_depth,
1206 upstream_non_memory,
1207 Some(lifecycle_config),
1208 mem_props.mp_learning_enabled,
1209 );
1210
1211 registered_count += 1;
1212 }
1213 }
1214 }
1215 }
1216 let _ = registered_count; }
1218 }
1219
1220 if expected_synapses > 0 {
1222 let diff = (total_synapses_created as i64 - expected_synapses as i64).abs();
1223 let diff_pct = (diff as f64 / expected_synapses.max(1) as f64) * 100.0;
1224
1225 if diff_pct > 10.0 {
1226 warn!(target: "feagi-bdu",
1227 "Synapse count variance: created {} but genome stats expected {} ({:.1}% difference)",
1228 total_synapses_created, expected_synapses, diff_pct
1229 );
1230 } else {
1231 info!(target: "feagi-bdu",
1232 "Synapse count matches genome stats within {:.1}% ({} vs {})",
1233 diff_pct, total_synapses_created, expected_synapses
1234 );
1235 }
1236 }
1237
1238 self.update_stage(DevelopmentStage::Synaptogenesis, 100);
1239 info!(target: "feagi-bdu"," ✅ Synaptogenesis complete: {} synapses created", total_synapses_created);
1240
1241 Ok(())
1242 }
1243
1244 fn rebuild_memory_twin_mappings_from_genome(
1245 &mut self,
1246 genome: &RuntimeGenome,
1247 ) -> BduResult<()> {
1248 use feagi_structures::genomic::cortical_area::CorticalAreaType;
1249 let mut repaired = 0usize;
1250
1251 for (memory_id, memory_area) in genome.cortical_areas.iter() {
1252 let is_memory = matches!(
1253 memory_area.cortical_id.as_cortical_type(),
1254 Ok(CorticalAreaType::Memory(_))
1255 ) || memory_area
1256 .properties
1257 .get("is_mem_type")
1258 .and_then(|v| v.as_bool())
1259 .unwrap_or(false)
1260 || memory_area
1261 .properties
1262 .get("cortical_group")
1263 .and_then(|v| v.as_str())
1264 .is_some_and(|v| v.eq_ignore_ascii_case("MEMORY"));
1265 if !is_memory {
1266 continue;
1267 }
1268
1269 let Some(dstmap) = memory_area
1270 .properties
1271 .get("cortical_mapping_dst")
1272 .and_then(|v| v.as_object())
1273 else {
1274 continue;
1275 };
1276
1277 for (dst_id_str, rules) in dstmap {
1278 let Some(rule_array) = rules.as_array() else {
1279 continue;
1280 };
1281 let has_replay = rule_array.iter().any(|rule| {
1282 rule.get("morphology_id")
1283 .and_then(|v| v.as_str())
1284 .is_some_and(|id| id == "memory_replay")
1285 });
1286 if !has_replay {
1287 continue;
1288 }
1289
1290 let dst_id = match CorticalID::try_from_base_64(dst_id_str) {
1291 Ok(id) => id,
1292 Err(_) => {
1293 warn!(
1294 target: "feagi-bdu",
1295 "Invalid twin cortical ID in memory_replay dstmap: {}",
1296 dst_id_str
1297 );
1298 continue;
1299 }
1300 };
1301
1302 let Some(twin_area) = genome.cortical_areas.get(&dst_id) else {
1303 continue;
1304 };
1305 let Some(upstream_id_str) = twin_area
1306 .properties
1307 .get("memory_twin_of")
1308 .and_then(|v| v.as_str())
1309 else {
1310 continue;
1311 };
1312 let upstream_id = match CorticalID::try_from_base_64(upstream_id_str) {
1313 Ok(id) => id,
1314 Err(_) => {
1315 warn!(
1316 target: "feagi-bdu",
1317 "Invalid memory_twin_of value on twin area {}: {}",
1318 dst_id.as_base_64(),
1319 upstream_id_str
1320 );
1321 continue;
1322 }
1323 };
1324
1325 let mut manager = self.connectome_manager.write();
1326 if let Err(e) = manager.ensure_memory_twin_area(memory_id, &upstream_id) {
1327 warn!(
1328 target: "feagi-bdu",
1329 "Failed to rebuild memory twin mapping for memory {} upstream {}: {}",
1330 memory_id.as_base_64(),
1331 upstream_id.as_base_64(),
1332 e
1333 );
1334 continue;
1335 }
1336 repaired += 1;
1337 }
1338 }
1339
1340 info!(
1341 target: "feagi-bdu",
1342 "Rebuilt {} memory twin mapping(s) from genome",
1343 repaired
1344 );
1345 Ok(())
1346 }
1347}
1348
1349fn estimate_synapses_for_area(
1353 src_area: &CorticalArea,
1354 genome: &feagi_evolutionary::RuntimeGenome,
1355) -> usize {
1356 let dstmap = match src_area.properties.get("cortical_mapping_dst") {
1357 Some(serde_json::Value::Object(map)) => map,
1358 _ => return 0,
1359 };
1360
1361 let mut total = 0;
1362
1363 for (dst_id, rules) in dstmap {
1364 let dst_cortical_id = match feagi_evolutionary::string_to_cortical_id(dst_id) {
1366 Ok(id) => id,
1367 Err(_) => continue,
1368 };
1369 let dst_area = match genome.cortical_areas.get(&dst_cortical_id) {
1370 Some(area) => area,
1371 None => continue,
1372 };
1373
1374 let rules_array = match rules.as_array() {
1375 Some(arr) => arr,
1376 None => continue,
1377 };
1378
1379 for rule in rules_array {
1380 let morphology_id = rule
1381 .get("morphology_id")
1382 .and_then(|v| v.as_str())
1383 .unwrap_or("unknown");
1384 let scalar = rule
1385 .get("morphology_scalar")
1386 .and_then(|v| v.as_i64())
1387 .unwrap_or(1) as usize;
1388
1389 let src_per_voxel = src_area
1391 .properties
1392 .get("neurons_per_voxel")
1393 .and_then(|v| v.as_u64())
1394 .unwrap_or(1) as usize;
1395 let dst_per_voxel = dst_area
1396 .properties
1397 .get("neurons_per_voxel")
1398 .and_then(|v| v.as_u64())
1399 .unwrap_or(1) as usize;
1400
1401 let src_voxels =
1402 src_area.dimensions.width * src_area.dimensions.height * src_area.dimensions.depth;
1403 let dst_voxels =
1404 dst_area.dimensions.width * dst_area.dimensions.height * dst_area.dimensions.depth;
1405
1406 let src_neurons = src_voxels as usize * src_per_voxel;
1407 let dst_neurons = dst_voxels as usize * dst_per_voxel as usize;
1408
1409 let count = match morphology_id {
1411 "block_to_block" => src_neurons * dst_per_voxel * scalar,
1412 "projector" | "transpose_xy" | "transpose_yz" | "transpose_xz"
1413 | "centered_projector" => src_neurons * dst_neurons * scalar,
1414 _ if morphology_id.contains("lateral") => src_neurons * scalar,
1415 _ => (src_neurons * scalar).min(src_neurons * dst_neurons / 10),
1416 };
1417
1418 total += count;
1419 }
1420 }
1421
1422 total
1423}
1424
1425impl Neuroembryogenesis {
1426 fn calculate_autogen_region_position(
1428 root_area_ids: &[CorticalID],
1429 genome: &feagi_evolutionary::RuntimeGenome,
1430 ) -> [i32; 3] {
1431 if root_area_ids.is_empty() {
1432 return [100, 0, 0];
1433 }
1434
1435 let mut min_x = i32::MAX;
1436 let mut max_x = i32::MIN;
1437 let mut min_y = i32::MAX;
1438 let mut max_y = i32::MIN;
1439 let mut min_z = i32::MAX;
1440 let mut max_z = i32::MIN;
1441
1442 for cortical_id in root_area_ids {
1443 if let Some(area) = genome.cortical_areas.get(cortical_id) {
1444 let pos: (i32, i32, i32) = area.position.into();
1445 let dims = (
1446 area.dimensions.width as i32,
1447 area.dimensions.height as i32,
1448 area.dimensions.depth as i32,
1449 );
1450
1451 min_x = min_x.min(pos.0);
1452 max_x = max_x.max(pos.0 + dims.0);
1453 min_y = min_y.min(pos.1);
1454 max_y = max_y.max(pos.1 + dims.1);
1455 min_z = min_z.min(pos.2);
1456 max_z = max_z.max(pos.2 + dims.2);
1457 }
1458 }
1459
1460 let bbox_width = (max_x - min_x).max(1);
1461 let padding = (bbox_width / 5).max(50);
1462 let autogen_x = max_x + padding;
1463 let autogen_y = (min_y + max_y) / 2;
1464 let autogen_z = (min_z + max_z) / 2;
1465
1466 info!(target: "feagi-bdu",
1467 " 📐 Autogen position: ({}, {}, {}) [padding: {}]",
1468 autogen_x, autogen_y, autogen_z, padding);
1469
1470 [autogen_x, autogen_y, autogen_z]
1471 }
1472
1473 fn analyze_region_io(
1479 region_area_ids: &[feagi_structures::genomic::cortical_area::CorticalID],
1480 all_cortical_areas: &std::collections::HashMap<CorticalID, CorticalArea>,
1481 ) -> (Vec<String>, Vec<String>) {
1482 let area_set: std::collections::HashSet<_> = region_area_ids.iter().cloned().collect();
1483 let mut inputs = Vec::new();
1484 let mut outputs = Vec::new();
1485
1486 let extract_destinations = |area: &CorticalArea| -> Vec<String> {
1488 area.properties
1489 .get("cortical_mapping_dst")
1490 .and_then(|v| v.as_object())
1491 .map(|obj| obj.keys().cloned().collect())
1492 .unwrap_or_default()
1493 };
1494
1495 for area_id in region_area_ids {
1497 if let Some(area) = all_cortical_areas.get(area_id) {
1498 let destinations = extract_destinations(area);
1499 let external_destinations: Vec<_> = destinations
1501 .iter()
1502 .filter_map(|dest| feagi_evolutionary::string_to_cortical_id(dest).ok())
1503 .filter(|dest_id| !area_set.contains(dest_id))
1504 .collect();
1505
1506 if !external_destinations.is_empty() {
1507 outputs.push(area_id.as_base_64());
1508 }
1509 }
1510 }
1511
1512 for (source_area_id, source_area) in all_cortical_areas.iter() {
1514 if area_set.contains(source_area_id) {
1516 continue;
1517 }
1518
1519 let destinations = extract_destinations(source_area);
1520 for dest_str in destinations {
1521 if let Ok(dest_id) = feagi_evolutionary::string_to_cortical_id(&dest_str) {
1522 if area_set.contains(&dest_id) {
1523 let dest_string = dest_id.as_base_64();
1524 if !inputs.contains(&dest_string) {
1525 inputs.push(dest_string);
1526 }
1527 }
1528 }
1529 }
1530 }
1531
1532 (inputs, outputs)
1533 }
1534
1535 fn update_stage(&self, stage: DevelopmentStage, progress: u8) {
1537 let mut p = self.progress.write();
1538 p.stage = stage;
1539 p.progress = progress;
1540 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1541 }
1542
1543 fn update_progress<F>(&self, f: F)
1545 where
1546 F: FnOnce(&mut DevelopmentProgress),
1547 {
1548 let mut p = self.progress.write();
1549 f(&mut p);
1550 p.duration_ms = self.start_time.elapsed().as_millis() as u64;
1551 }
1552}
1553
1554#[cfg(test)]
1555mod tests {
1556 use super::*;
1557 use feagi_evolutionary::create_genome_with_core_morphologies;
1558 use feagi_structures::genomic::cortical_area::CorticalAreaDimensions;
1559
1560 #[test]
1561 fn test_neuroembryogenesis_creation() {
1562 let manager = ConnectomeManager::instance();
1563 let neuro = Neuroembryogenesis::new(manager);
1564
1565 let progress = neuro.get_progress();
1566 assert_eq!(progress.stage, DevelopmentStage::Initialization);
1567 assert_eq!(progress.progress, 0);
1568 }
1569
1570 #[test]
1571 fn autogen_subregion_display_name_uses_title_when_meaningful() {
1572 assert_eq!(
1573 autogen_subregion_display_name("My Shared Circuit"),
1574 "My Shared Circuit"
1575 );
1576 }
1577
1578 #[test]
1579 fn autogen_subregion_display_name_falls_back_for_untitled() {
1580 assert_eq!(
1581 autogen_subregion_display_name("Untitled"),
1582 "Autogen Circuit"
1583 );
1584 assert_eq!(
1585 autogen_subregion_display_name("untitled"),
1586 "Autogen Circuit"
1587 );
1588 }
1589
1590 #[test]
1591 fn autogen_subregion_display_name_falls_back_for_blank() {
1592 assert_eq!(autogen_subregion_display_name(""), "Autogen Circuit");
1593 assert_eq!(autogen_subregion_display_name(" "), "Autogen Circuit");
1594 }
1595
1596 #[test]
1597 fn test_development_from_minimal_genome() {
1598 ConnectomeManager::reset_for_testing(); let manager = ConnectomeManager::instance();
1600 let mut neuro = Neuroembryogenesis::new(manager.clone());
1601
1602 let mut genome = create_genome_with_core_morphologies(
1604 "test_genome".to_string(),
1605 "Test Genome".to_string(),
1606 );
1607
1608 let cortical_id = CorticalID::try_from_bytes(b"cst_neur").unwrap(); let cortical_type = cortical_id
1610 .as_cortical_type()
1611 .expect("Failed to get cortical type");
1612 let area = CorticalArea::new(
1613 cortical_id,
1614 0,
1615 "Test Area".to_string(),
1616 CorticalAreaDimensions::new(10, 10, 10).unwrap(),
1617 (0, 0, 0).into(),
1618 cortical_type,
1619 )
1620 .expect("Failed to create cortical area");
1621 genome.cortical_areas.insert(cortical_id, area);
1622
1623 let result = neuro.develop_from_genome(&genome);
1625 assert!(result.is_ok(), "Development failed: {:?}", result);
1626
1627 let progress = neuro.get_progress();
1629 assert_eq!(progress.stage, DevelopmentStage::Completed);
1630 assert_eq!(progress.progress, 100);
1631 assert_eq!(progress.cortical_areas_created, 1);
1632
1633 println!("✅ Development completed in {}ms", progress.duration_ms);
1637 }
1638}