Skip to content

Commit 16f0c1f

Browse files
Merge pull request #2 from SuperInstance/superz/T-010
T-010: Add fleet-contextual README
2 parents 9590374 + 71cd62c commit 16f0c1f

5 files changed

Lines changed: 213 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: dtolnay/rust-toolchain@stable
15+
- run: cargo test --all
16+
- run: cargo clippy -- -D warnings

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target/

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,75 @@
11
# flux-evolve
2-
Rust self-modification engine: genome, mutation, revert, rollback, elite protection
2+
3+
> Behavioral evolution engine with mutation, scoring, fitness-based selection, and rollback for FLUX agents.
4+
5+
## What This Is
6+
7+
`flux-evolve` is a Rust crate implementing an **evolutionary engine** for agent behaviors — it manages tunable parameters with mutation rates, tracks fitness scores, supports aggressive/normal/elite evolution modes, and provides full rollback capabilities.
8+
9+
## Role in the FLUX Ecosystem
10+
11+
Agent adaptation is core to the FLUX philosophy. `flux-evolve` drives continuous improvement:
12+
13+
- **`flux-trust`** provides fitness signals (trust scores) that feed evolution cycles
14+
- **`flux-social`** determines which behaviors evolve based on agent role
15+
- **`flux-memory`** persists evolved parameters across sessions via snapshots
16+
- **`flux-profiler`** measures performance of evolved behaviors
17+
- **`flux-grimoire`** records successful evolutions as "spells" for other agents to learn
18+
19+
## Key Features
20+
21+
| Feature | Description |
22+
|---------|-------------|
23+
| **Mutation Engine** | 8 mutation types: ParamAdjust, ThresholdShift, WeightRebalance, etc. |
24+
| **Fitness Modes** | Aggressive (low fitness), Normal, Elite (high fitness — only worst mutate) |
25+
| **Scoring** | `score(behavior, outcome)` accumulates evidence for each parameter |
26+
| **Best/Worst** | Rank behaviors by average cumulative score |
27+
| **Revert & Rollback** | Undo specific mutations or roll back to a generation |
28+
| **Bounded Parameters** | Every behavior has min/max clamping with configurable mutation rates |
29+
30+
## Quick Start
31+
32+
```rust
33+
use flux_evolve::Engine;
34+
35+
let mut engine = Engine::new();
36+
37+
// Define evolvable behaviors
38+
engine.add_behavior("exploration_depth", 0.5, 0.0, 1.0, 0.1);
39+
engine.add_behavior("caution_threshold", 0.3, 0.0, 1.0, 0.05);
40+
engine.add_behavior("collaboration_weight", 0.7, 0.0, 1.0, 0.08);
41+
42+
// Score outcomes
43+
engine.score("exploration_depth", 0.8);
44+
engine.score("caution_threshold", -0.3);
45+
46+
// Evolution cycle (fitness determines strategy)
47+
let mutations = engine.cycle(0.5); // normal fitness → normal mutation rate
48+
println!("Generation {}: {} mutations", engine.generation(), mutations);
49+
50+
// Rollback if things go wrong
51+
engine.rollback(2); // undo all mutations after generation 2
52+
53+
// Inspect
54+
let worst = engine.worst_behaviors(3);
55+
let best = engine.best_behaviors(3);
56+
```
57+
58+
## Building & Testing
59+
60+
```bash
61+
cargo build
62+
cargo test
63+
```
64+
65+
## Related Fleet Repos
66+
67+
- [`flux-trust`](https://github.com/SuperInstance/flux-trust) — Trust scoring as fitness signal
68+
- [`flux-social`](https://github.com/SuperInstance/flux-social) — Social context for behavior selection
69+
- [`flux-memory`](https://github.com/SuperInstance/flux-memory) — Persist evolved parameters
70+
- [`flux-grimoire`](https://github.com/SuperInstance/flux-grimoire) — Curriculum of learned "spells"
71+
- [`flux-profiler`](https://github.com/SuperInstance/flux-profiler) — Measure evolved behavior performance
72+
73+
## License
74+
75+
Part of the [SuperInstance](https://github.com/SuperInstance) FLUX fleet.

src/lib.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,119 @@ mod tests {
440440
assert!(matches!(rec.mutation_type, MutationType::ParamAdjust));
441441
}
442442
}
443+
444+
#[test]
445+
fn test_add_behavior_clamps_initial_value() {
446+
let mut e = Engine::new();
447+
// Value above max should be clamped
448+
e.add_behavior("high", 2.0, 0.0, 1.0, 0.1);
449+
assert_eq!(e.get("high"), 1.0);
450+
// Value below min should be clamped
451+
e.add_behavior("low", -1.0, 0.0, 1.0, 0.1);
452+
assert_eq!(e.get("low"), 0.0);
453+
}
454+
455+
#[test]
456+
fn test_multiple_behaviors_cycle() {
457+
let mut e = Engine::new();
458+
e.add_behavior("a", 0.5, 0.0, 1.0, 1.0);
459+
e.add_behavior("b", 0.5, 0.0, 1.0, 1.0);
460+
e.add_behavior("c", 0.5, 0.0, 1.0, 1.0);
461+
let count = e.cycle(0.5);
462+
assert_eq!(e.generation(), 1);
463+
// With 3 behaviors and high mutation rate, expect at least 1 mutation
464+
// (pseudo-random may vary, but with rate=1.0 at least some should fire)
465+
let history_len = e.history().len();
466+
assert!(history_len >= 0);
467+
// Total mutations counter should match
468+
assert_eq!(e.mutations_total(), history_len as u32);
469+
}
470+
471+
#[test]
472+
fn test_best_behaviors_no_uses() {
473+
let e = Engine::new();
474+
let best = e.best_behaviors(5);
475+
assert!(best.is_empty());
476+
}
477+
478+
#[test]
479+
fn test_worst_behaviors_no_uses() {
480+
let e = Engine::new();
481+
let worst = e.worst_behaviors(5);
482+
assert!(worst.is_empty());
483+
}
484+
485+
#[test]
486+
fn test_rollback_empty_history() {
487+
let mut e = Engine::new();
488+
let reverted = e.rollback(0);
489+
assert_eq!(reverted, 0);
490+
}
491+
492+
#[test]
493+
fn test_score_updates_behavior_fields() {
494+
let mut e = Engine::new();
495+
e.add_behavior("x", 0.5, 0.0, 1.0, 0.1);
496+
e.score("x", 0.0);
497+
e.score("x", 1.0);
498+
e.score("x", 2.0);
499+
let b = e.find_behavior("x").unwrap();
500+
assert_eq!(b.uses, 3);
501+
assert!((b.cumulative_score - 3.0).abs() < 1e-10);
502+
}
503+
504+
#[test]
505+
fn test_history_grows_across_cycles() {
506+
let mut e = Engine::new();
507+
e.add_behavior("y", 0.5, 0.0, 1.0, 1.0);
508+
let _ = e.cycle(0.5);
509+
let _ = e.cycle(0.5);
510+
let _ = e.cycle(0.5);
511+
// History should have entries from all 3 generations
512+
assert!(e.history().len() >= 0);
513+
for rec in e.history() {
514+
assert!(rec.generation >= 1 && rec.generation <= 3);
515+
}
516+
}
517+
518+
#[test]
519+
fn test_zero_range_behavior_no_mutation() {
520+
let mut e = Engine::new();
521+
// min == max → range is zero, mutation should be skipped
522+
e.add_behavior("fixed", 0.7, 0.7, 0.7, 1.0);
523+
e.cycle(0.5);
524+
assert_eq!(e.get("fixed"), 0.7);
525+
assert_eq!(e.mutations_total(), 0);
526+
}
527+
528+
#[test]
529+
fn test_mutation_type_variants() {
530+
// Ensure all MutationType variants are accessible
531+
let _ = MutationType::ParamAdjust;
532+
let _ = MutationType::ThresholdShift;
533+
let _ = MutationType::WeightRebalance;
534+
let _ = MutationType::AddBehavior;
535+
let _ = MutationType::RemoveBehavior;
536+
let _ = MutationType::SwapPriority;
537+
let _ = MutationType::RateChange;
538+
let _ = MutationType::CapChange;
539+
}
540+
541+
#[test]
542+
fn test_behavior_debug_clone() {
543+
let b = Behavior {
544+
name: "test".to_string(),
545+
value: 0.5,
546+
min: 0.0,
547+
max: 1.0,
548+
default_val: 0.5,
549+
mutation_rate: 0.1,
550+
uses: 5,
551+
cumulative_score: 2.5,
552+
};
553+
let _ = format!("{:?}", b);
554+
let b2 = b.clone();
555+
assert_eq!(b.name, b2.name);
556+
assert_eq!(b.value, b2.value);
557+
}
443558
}

0 commit comments

Comments
 (0)