diff --git a/Cargo.toml b/Cargo.toml
index c739de37f78ba1f990654be546c9028aeed66ab7..ab4404d012f8339ac971853d40d84da97fd37501 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,10 +12,5 @@ repository = "https://github.com/zkat/big-brain"
 homepage = "https://github.com/zkat/big-brain"
 
 [dependencies]
-big-brain-derive = { path = "./big-brain-derive", version = "0.2" }
-serde = "1.0.111"
-typetag = "0.1.5"
-specs-derive = "0.4.1"
-ron = "0.6.0"
 bevy = "0.5.0"
 
diff --git a/big-brain-derive/Cargo.toml b/big-brain-derive/Cargo.toml
deleted file mode 100644
index 4a0a22a9203cc103a7e5898b6804f12145b89181..0000000000000000000000000000000000000000
--- a/big-brain-derive/Cargo.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[package]
-name = "big-brain-derive"
-version = "0.2.1"
-authors = ["Kat Marchán <kzm@zkat.tech>"]
-edition = "2018"
-keywords = ["utility-ai", "ai", "bevy", "ecs"]
-categories = ["game-development"]
-description = "Derive macros for the big-brain Utility AI library"
-license-file = "../LICENSE.md"
-repository = "https://github.com/zkat/big-brain"
-homepage = "https://github.com/zkat/big-brain"
-
-[lib]
-proc-macro = true
-
-[dependencies]
-syn = "1.0.33"
-quote = "1.0.7"
-proc-macro2 = "1.0.18"
-darling = "0.10.2"
diff --git a/big-brain-derive/src/action.rs b/big-brain-derive/src/action.rs
deleted file mode 100644
index e03bd477b246204695efb9c6b775aeeadce86740..0000000000000000000000000000000000000000
--- a/big-brain-derive/src/action.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-use darling::{ast, FromDeriveInput, FromField, ToTokens};
-use proc_macro2::TokenStream;
-use quote::quote;
-
-#[derive(Debug, FromDeriveInput)]
-#[darling(attributes(action))]
-pub struct Action {
-    ident: syn::Ident,
-    generics: syn::Generics,
-    data: ast::Data<(), ActionField>,
-}
-
-#[derive(Debug, FromField)]
-#[darling(attributes(action))]
-struct ActionField {
-    ident: Option<syn::Ident>,
-    ty: syn::Type,
-    #[darling(default)]
-    param: bool,
-    #[darling(default)]
-    default: bool,
-}
-
-impl ToTokens for Action {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        let Action {
-            ref ident,
-            ref data,
-            ..
-        } = *self;
-        let fields = data
-            .as_ref()
-            .take_struct()
-            .expect("Enums not supported")
-            .fields;
-        let field_defs = fields.clone().into_iter().filter_map(|field| {
-            let ActionField {
-                ident, ty, param, ..
-            } = field;
-            let ident = ident.clone().unwrap();
-            if *param && ident != syn::Ident::new("actor", ident.span()) {
-                Some(quote! { #ident: #ty })
-            } else {
-                None
-            }
-        });
-        let field_assignments = fields.into_iter().map(|field| {
-            let ActionField {
-                ident,
-                param,
-                default,
-                ..
-            } = field;
-            let ident = ident.clone().unwrap();
-            if *param {
-                quote! {
-                    #ident: self.#ident
-                }
-            } else if *default {
-                quote! {
-                    #ident: ::core::default::Default::default()
-                }
-            } else if ident == syn::Ident::new("actor", ident.span()) {
-                quote! {
-                    #ident: actor
-                }
-            } else {
-                panic!("Field not state, default, or param: {}", ident);
-            }
-        });
-        let ts = quote! {
-            mod big_brain_action_builder {
-                use super::#ident as Comp;
-
-                use big_brain::{typetag, serde::Deserialize, Action, ActionRunner, bevy::prelude::{Entity, Commands}, ActionEnt};
-
-                #[derive(Debug, Deserialize)]
-                struct #ident {
-                    #(#field_defs),*
-                }
-
-                #[typetag::deserialize]
-                impl Action for #ident {
-                    fn build(self: Box<Self>, actor: Entity, action_ent: ActionEnt, cmd: &mut Commands) -> Box<dyn ActionRunner> {
-                        self
-                    }
-                }
-
-                impl ActionRunner for #ident {
-                    fn activate(&self, actor: Entity, action_ent: ActionEnt, cmd: &mut Commands) {
-                        cmd.entity(action_ent.0).insert(Comp {
-                            #(#field_assignments),*
-                        });
-                    }
-                    fn deactivate(&self, action_ent: ActionEnt, cmd: &mut Commands) {
-                        cmd.entity(action_ent.0).remove::<Comp>();
-                    }
-                }
-            }
-        };
-        tokens.extend(ts);
-    }
-}
diff --git a/big-brain-derive/src/lib.rs b/big-brain-derive/src/lib.rs
deleted file mode 100644
index 24e9d814afefa51c255498c57ae2d56e49940699..0000000000000000000000000000000000000000
--- a/big-brain-derive/src/lib.rs
+++ /dev/null
@@ -1,206 +0,0 @@
-use darling::FromDeriveInput;
-use quote::quote;
-use syn::{parse_macro_input, DeriveInput};
-
-use action::Action;
-use scorer::Scorer;
-
-mod action;
-mod scorer;
-
-/**
-`Action`s in `big-brain` are defined through this derive macro. Once defined,
-they can be freely used in a .ron file. They define actual behaviors that an
-`actor` will perform when the Thinker engine picks it as the active action,
-based on [Considerations](derive.Consideration.html).
-
-## Definition Example
-
-```ignore
-use specs::{Component, Entity, System, WriteStorage};
-use big_brain::{Action, ActionState};
-
-// These are your game's components.
-use crate::components;
-
-// This will be used to create `Action` components. They MUST implement the
-// `specs::Component` trait.
-#[derive(Debug, Clone, Component, Action)]
-pub struct Eat {
-    // All actions **must** have a public `actor` field. This will be populated
-    // with the actual actor performing the Action. The `Entity` associated with
-    // the `Action` itself is distinct from the actor.
-    pub actor: Entity,
-
-    // `default` fields will be populated using default::Default() when the
-    // Action is instantiated. These cannot be used as params.
-    #[action(default)]
-    pub foo: f32,
-
-    // `param` fields will be populated using the value passed in through the
-    // `.ron` file.
-    #[action(param)]
-    pub reduce_by: f32,
-}
-
-// Once an Action component is defined, we define a System that can act on it.
-pub struct EatSystem;
-
-impl<'a> System<'a> for EatSystem {
-    type SystemData = (
-        WriteStorage<'a, components::Hunger>,
-        // This is the actual Eat component.
-        WriteStorage<'a, Eat>,
-        // An ActionState component is attached to every Action Entity.
-        // It contains the current running status of the Action, and will be
-        // updated as needed by the actor's Thinker.
-        WriteStorage<'a, ActionState>,
-    );
-    fn run(&mut self, (mut hungers, mut eat_actions, mut states): Self::SystemData) {
-        // You can join the Eat and ActionState together. They're on the same component.
-        for (state, eat_action) in (&mut states, &mut eat_actions).join() {
-            // Any components attached to the actor must be fetched separately.
-            if let Some(hunger) = hungers.get_mut(eat_action.actor.clone()) {
-                match state {
-                    // At the very least, every Action should handle the
-                    // `Requested` state.
-                    ActionState::Requested => {
-                        hunger.hunger -= eat_action.reduce_by;
-                        // Success tells the Thinker that this action succeeded!
-                        *state = ActionState::Success;
-                    }
-                    // Make sure to handle Cancelled for long-running Actions.
-                    // The Thinker will not continue until the state is either
-                    // Success or Failure.
-                    ActionState::Cancelled => {
-                        *state = ActionState::Failure;
-                    }
-                    _ => {}
-                }
-            }
-        }
-    }
-}
-```
-
-## Usage Example
-
-```ignore
-(
-    picker: {"FirstToScore": ()},
-    // Actions are defined using the `then` param to Choices
-    choices: [(
-        consider: [{"Hunger": ()}],
-        // We can use the param defined in our derive definition here.
-        // The `foo` field will be defaulted and cannot be defined here.
-        then: {"Eat": (reduce_by: 80.0)},
-    )]
-)
-```
-
-*/
-#[proc_macro_derive(Action, attributes(action))]
-pub fn derive_action_builder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    let input = parse_macro_input!(input as DeriveInput);
-    let action = Action::from_derive_input(&input).unwrap();
-    quote!(#action).into()
-}
-
-/**
-`Consideration`s in `big-brain` are defined through this derive macro. Once defined,
-they can be freely used in a .ron file. While `Action`s define behaviors,
-`Consideration`s are used to determine _whether_ to execute a certain action.
-
-`Consideration`s are responsible for determining a specific `Utility`, or score,
-in Utility AI terms. This score is what sets Utility AI apart from plain old
-Behavior Trees.
-
-Like anything else in an Entity system, considerations and their behaviors
-consist of a `Component` and an associated `System`.
-
-## Definition Example
-
-```ignore
-use specs::{Component, Entity, ReadStorage, System, WriteStorage};
-use big_brain::{Consideration, Utility};
-
-// These are your game's components.
-use crate::components;
-
-// `Consideration`s are defined by deriving them -- they MUST be Components.
-#[derive(Debug, Component, Consideration)]
-pub struct Hunger {
-    // All considerations **must** have a public `actor` field. This will be populated
-    // with the actual actor considering the world around it The `Entity` associated with
-    // the `Consideration` itself is distinct from the actor.
-    pub actor: Entity,
-
-    // `default` fields will be populated using default::Default() when the
-    // Consideration is instantiated. These cannot be used as params.
-    #[consideration(default)]
-    pub evaluator: PowerEvaluator,
-
-    // `param` fields will be populated using the value passed in through the
-    // `.ron` file.
-    #[consideration(param)]
-    pub weight: f32,
-}
-
-pub struct ConsiderHunger;
-
-impl<'a> System<'a> for ConsiderHunger {
-    type SystemData = (
-        ReadStorage<'a, components::Hunger>,
-
-        // This is the actual `Consideration` component.
-        WriteStorage<'a, Hunger>,
-
-        // The `Utility` component associated with this `Consideration` holds
-        // the current calculated score for that consideration.
-        WriteStorage<'a, Utility>,
-    );
-
-    fn run(&mut self, (hungers, mut considerers, mut utilities): Self::SystemData) {
-        // Join the considerations with the utilities -- they share an `Entity`.
-        for (conser, util) in (&mut considerers, &mut utilities).join() {
-            // Any actor-related components must be fetched separately, based on
-            // the consideration's `actor`.
-            if let Some(hunger) = hungers.get(conser.actor.clone()) {
-                *util = Utility {
-                    // values and weights can be arbitrary numbers. The final
-                    // score is based on combining these two values.
-                    //
-                    // Utilities with weight `0.0` are not used.
-                    //
-                    // For the formula, refer to the docs on `WeightedMeasure`.
-                    value: conser.evaluator.evaluate(hunger.hunger),
-                    weight: conser.weight,
-                };
-            }
-        }
-    }
-}
-```
-
-## Usage Example
-
-```ignore
-(
-    picker: {"FirstToScore": ()},
-    choices: [(
-        // Considerations to use are defined using the `consider` param in choices.
-        // A choice can have zero or more considerations.
-        consider: [{"Hunger": (weight: 1.0)}],
-
-        // This is the action that will be executed if this choice "wins".
-        then: {"Eat": ()},
-    )]
-)
-```
-*/
-#[proc_macro_derive(Scorer, attributes(scorer))]
-pub fn derive_scorer_builder(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-    let input = parse_macro_input!(input as DeriveInput);
-    let scorer = Scorer::from_derive_input(&input).unwrap();
-    (quote!(#scorer)).into()
-}
diff --git a/big-brain-derive/src/scorer.rs b/big-brain-derive/src/scorer.rs
deleted file mode 100644
index 824dd10e650668e83ece7ab1113c74beb33d9e5c..0000000000000000000000000000000000000000
--- a/big-brain-derive/src/scorer.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use darling::{ast, FromDeriveInput, FromField, ToTokens};
-use proc_macro2::TokenStream;
-use quote::quote;
-
-#[derive(Debug, FromDeriveInput)]
-#[darling(attributes(scorer))]
-pub struct Scorer {
-    ident: syn::Ident,
-    generics: syn::Generics,
-    data: ast::Data<(), ScorerField>,
-}
-
-#[derive(Debug, FromField)]
-#[darling(attributes(scorer))]
-struct ScorerField {
-    ident: Option<syn::Ident>,
-    ty: syn::Type,
-    #[darling(default)]
-    param: bool,
-    #[darling(default)]
-    default: bool,
-}
-
-impl ToTokens for Scorer {
-    fn to_tokens(&self, tokens: &mut TokenStream) {
-        let Scorer {
-            ref ident,
-            ref data,
-            ..
-        } = *self;
-        let fields = data
-            .as_ref()
-            .take_struct()
-            .expect("Enums not supported")
-            .fields;
-        let field_defs = fields.clone().into_iter().filter_map(|field| {
-            let ScorerField {
-                ident, ty, param, ..
-            } = field;
-            let ident = ident.clone().unwrap();
-            if *param && ident != syn::Ident::new("parent", ident.span()) {
-                Some(quote! { #ident: #ty })
-            } else {
-                None
-            }
-        });
-        let field_assignments = fields.into_iter().map(|field| {
-            let ScorerField {
-                ident,
-                param,
-                default,
-                ..
-            } = field;
-            let ident = ident.clone().unwrap();
-            if *param {
-                quote! {
-                    #ident: self.#ident
-                }
-            } else if *default {
-                quote! {
-                    #ident: ::core::default::Default::default()
-                }
-            } else if ident == syn::Ident::new("actor", ident.span()) {
-                quote! {
-                    #ident: actor
-                }
-            } else {
-                panic!("Field not state, default, or param: {}", ident);
-            }
-        });
-        let ts = quote! {
-            mod big_brain_scorer_builder {
-                use super::#ident as Comp;
-
-                use big_brain::{typetag, serde::Deserialize, Scorer, bevy::prelude::*, ScorerEnt};
-                // use typetag;
-
-                #[derive(Debug, Deserialize)]
-                struct #ident {
-                    #(#field_defs),*
-                }
-                #[typetag::deserialize]
-                impl Scorer for #ident {
-                    fn build(&self, actor: Entity, cmd: &mut Commands) -> ScorerEnt {
-                        let ent = ScorerEnt(cmd.spawn().id());
-                        cmd.entity(ent.0)
-                        .insert(big_brain::Score::default())
-                        .insert(Comp {
-                            #(#field_assignments),*
-                        });
-                        cmd.entity(actor).push_children(&[ent.0]);
-                        ent
-                    }
-                }
-            }
-        };
-        tokens.extend(ts);
-    }
-}
diff --git a/examples/basic.ron b/examples/basic.ron
deleted file mode 100644
index 1dfd451c11e3bbf389a985ee347fcb8fb35ecdb4..0000000000000000000000000000000000000000
--- a/examples/basic.ron
+++ /dev/null
@@ -1,25 +0,0 @@
-(
-    // We have to use these special "externally tagged" specifiers instead of,
-    // say, `FirstToScore(threshold: 80.0)` due to a bug in RON itself:
-    // https://github.com/ron-rs/ron/issues/123
-    picker: {"FirstToScore": (threshold: 80.0)},
-    choices: [(
-        when: {"Bladder": ()},
-        // Thinkers happen to also be actions, so you can nest theem!
-        then: {"Thinker": (
-            picker: {"FirstToScore": (threshold: 80.0)},
-            choices: [(
-                when: [{"Bladder": ()}],
-                then: {"Pee": ()}
-            )]
-        )}
-    ), (
-        when: {"Thirst": ()},
-        then: {"Drink": ()},
-    ), (
-        when: {"Hunger": ()},
-        then: {"Eat": ()},
-    )],
-    otherwise: Some({"Meander": ()}),
-)
-
diff --git a/examples/thirst.rs b/examples/thirst.rs
index b0e54154831609946dd99b99a3b644bfe6f50ab5..b9bab55cd44e0af7546f2acf4b102204a9b2aa81 100644
--- a/examples/thirst.rs
+++ b/examples/thirst.rs
@@ -31,16 +31,37 @@ pub fn thirst_system(time: Res<Time>, mut thirsts: Query<&mut Thirst>) {
 // do it? This is the first bit involving Big Brain itself, and there's a few
 // pieces you need:
 
-// First, you need an Action struct, and derive Action.
+// First, you need an Action and an ActionBuilder struct.
 //
 // These actions will be spawned and queued by the game engine when their
 // conditions trigger (we'll configure what these are later).
-#[derive(Debug, Action)]
+#[derive(Debug, Clone)]
 pub struct Drink;
 
+// The convention is to attach a `::build()` function to the Action type.
+impl Drink {
+    pub fn build() -> DrinkBuilder {
+        DrinkBuilder
+    }
+}
+
+// Then we define an ActionBuilder, which is responsible for making new
+// Action components for us.
+#[derive(Debug, Clone)]
+pub struct DrinkBuilder;
+
+// All you need to implement heree is the `build()` method, which requires
+// that you attach your actual component to the action Entity that was created
+// and configured for you.
+impl ActionBuilder for DrinkBuilder {
+    fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) {
+        cmd.entity(action).insert(Drink);
+    }
+}
+
 // Associated with that Drink Action, you then need to have a system that will
 // actually execute those actions when they're "spawned" by the Big Brain
-// engine.
+// engine. This is the actual "act" part of the Action.
 //
 // In our case, we want the Thirst components, since we'll be changing those.
 // Additionally, we want to pick up the DrinkAction components, as well as
@@ -81,17 +102,32 @@ fn drink_action_system(
 // run in the background, calculating a "Score" value, which is what Big Brain
 // will use to pick which actions to execute.
 //
-// Additionally, though, we pull in an evaluator and define a weight. Which is
-// just mathy stuff you can tweak to get the behavior you want. More on this
-// in the docs (later), but for now, just put them in there and trust the
-// system. :)
-#[derive(Debug, Scorer)]
+// Just like with Actions, we use the convention of having separate
+// ScorerBuilder and Scorer components. While it might seem like a lot of
+// boilerplate, in a "real" application, you will almost certainly have data
+// and configuration concerns. This pattern separates those nicely.
+#[derive(Debug, Clone)]
 pub struct Thirsty;
 
-// Look familiar? Similar dance to Actions here.
+impl Thirsty {
+    fn build() -> ThirstyBuilder {
+        ThirstyBuilder
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct ThirstyBuilder;
+
+impl ScorerBuilder for ThirstyBuilder {
+    fn build(&self, cmd: &mut Commands, scorer: Entity, _actor: Entity) {
+        cmd.entity(scorer).insert(Thirsty);
+    }
+}
+
+// Looks familiar? It's a lot likee Actions!
 pub fn thirsty_scorer_system(
     thirsts: Query<&Thirst>,
-    // Same dance with the Parent here, but now we've added a Score!
+    // Same dance with the Parent here, but now Big Brain has added a Score component!
     mut query: Query<(&Parent, &mut Score), With<Thirsty>>,
 ) {
     for (Parent(actor), mut score) in query.iter_mut() {
@@ -109,36 +145,21 @@ pub fn thirsty_scorer_system(
     }
 }
 
-// Now that we hav eall that defined, it's time to add a Thinker to an entity!
+// Now that we have all that defined, it's time to add a Thinker to an entity!
 // The Thinker is the actual "brain" behind all the AI. Every entity you want
 // to have AI behavior should have one *or more* Thinkers attached to it.
-// Thinkers are configured using RON right now, with a DSL that makes it easy
-// to define, in data, the actual behavior you want.
 pub fn init_entities(mut cmd: Commands) {
+    // Create the entity and throw the Thirst component in there. Nothing special here.
     let actor = cmd.spawn().insert(Thirst::new(70.0, 2.0)).id();
 
-    // Here's a very simple one that only has one scorer and one
-    // associated action. But you can have more of them, and even nest them by
-    // using more Thinkers (which are actually themselves Actions). See
-    // basic.ron in examples/ for a more involved Thinker definition.
-    //
-    // Ultimately, these Thinkers are meant to be usable by non-programmers:
-    // You, the developer, create Actions and Scorers, and someone else
-    // is then able to put them all together like LEGOs into all sorts of
-    // intricate logic.
-    Thinker::load_from_str(
-        r#"
-(
-    picker: {"FirstToScore": (threshold: 80.0)},
-    choices: [(
-        when: {"Thirsty": ()},
-        // This action will fire when (and as long as) Thirsty scores >=80.0.
-        then: {"Drink": ()},
-    )],
-)
-"#,
-    )
-    .build(actor, &mut cmd);
+    // And finally, we put all the pieces together!
+    Thinker::build()
+        .picker(FirstToScore { threshold: 80.0 })
+        // Note that what we pass in are _builders_, not components!
+        .when(Thirsty::build(), Drink::build())
+        // .attach will do all the necessary work of attaching this component
+        // and hooking it up to the AI system.
+        .attach(&mut cmd, actor);
 }
 
 fn main() {
diff --git a/src/actions.rs b/src/actions.rs
index 6b8b2a7e7810f9532fc539b8a9071acc4a3b19c3..336540b989497926a1c886c4a397bc09bc0754c7 100644
--- a/src/actions.rs
+++ b/src/actions.rs
@@ -1,10 +1,9 @@
+use std::sync::Arc;
+
 use bevy::prelude::*;
 
 use crate::ActionEnt;
 
-#[derive(Debug)]
-pub struct ActionRunnerWrapper(pub(crate) Box<dyn ActionRunner>);
-
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum ActionState {
     Init,
@@ -15,57 +14,80 @@ pub enum ActionState {
     Failure,
 }
 
+impl Default for ActionState {
+    fn default() -> Self {
+        Self::Init
+    }
+}
+
 impl ActionState {
     pub fn new() -> Self {
         Self::default()
     }
+}
 
-    pub(crate) fn build(builder: Box<dyn Action>, actor: Entity, cmd: &mut Commands) -> ActionEnt {
-        let action_ent = ActionEnt(cmd.spawn().id());
-        let manager_wrapper = ActionRunnerWrapper(builder.build(actor, action_ent, cmd));
-        cmd.entity(action_ent.0)
-            .insert(ActionState::default())
-            .insert(manager_wrapper);
-        cmd.entity(actor).push_children(&[action_ent.0]);
-        action_ent
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ActionBuilderId;
+
+#[derive(Debug, Clone)]
+pub struct ActionBuilderWrapper(pub ActionBuilderId, pub Arc<dyn ActionBuilder>);
+
+impl ActionBuilderWrapper {
+    pub fn new(builder: Arc<dyn ActionBuilder>) -> Self {
+        ActionBuilderWrapper(ActionBuilderId, builder)
     }
 }
 
-impl Default for ActionState {
-    fn default() -> Self {
-        Self::Init
+pub trait ActionBuilder: std::fmt::Debug + Send + Sync {
+    fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity);
+    fn attach(&self, cmd: &mut Commands, actor: Entity) -> Entity {
+        let action_ent = ActionEnt(cmd.spawn().id());
+        cmd.entity(action_ent.0).insert(ActionState::new());
+        cmd.entity(actor).push_children(&[action_ent.0]);
+        self.build(cmd, action_ent.0, actor);
+        action_ent.0
     }
 }
 
-/**
-This trait defines new actions. In general, you should use the [derive macro](derive.Action.html) instead.
-*/
-#[typetag::deserialize]
-pub trait Action: std::fmt::Debug + Send + Sync {
-    fn build(
-        self: Box<Self>,
-        actor: Entity,
-        action_ent: ActionEnt,
-        cmd: &mut Commands,
-    ) -> Box<dyn ActionRunner>;
+#[derive(Debug)]
+pub struct StepsBuilder {
+    steps: Vec<Arc<dyn ActionBuilder>>,
 }
 
-pub trait ActionRunner: std::fmt::Debug + Send + Sync {
-    fn activate(&self, actor: Entity, action: ActionEnt, cmd: &mut Commands);
-    fn deactivate(&self, action: ActionEnt, cmd: &mut Commands);
+impl StepsBuilder {
+    pub fn step(&mut self, action_builder: impl ActionBuilder + 'static) -> &mut Self {
+        self.steps.push(Arc::new(action_builder));
+        self
+    }
 }
 
+impl ActionBuilder for StepsBuilder {
+    fn build(&self, cmd: &mut Commands, action: Entity, actor: Entity) {
+        let child_action = self.steps[0].attach(cmd, actor);
+        cmd.entity(action).insert(Steps {
+            active_step: 0,
+            active_ent: ActionEnt(child_action),
+            steps: self.steps.clone(),
+        });
+    }
+}
 #[derive(Debug)]
 pub struct Steps {
-    steps: Vec<ActionEnt>,
+    steps: Vec<Arc<dyn ActionBuilder>>,
     active_step: usize,
+    active_ent: ActionEnt,
+}
+
+impl Steps {
+    pub fn build() -> StepsBuilder {
+        StepsBuilder { steps: Vec::new() }
+    }
 }
 
 pub fn steps_system(
     mut cmd: Commands,
     mut steps_q: Query<(Entity, &Parent, &mut Steps)>,
     mut states: Query<&mut ActionState>,
-    runners: Query<&ActionRunnerWrapper>,
 ) {
     use ActionState::*;
     for (seq_ent, Parent(actor), mut steps_action) in steps_q.iter_mut() {
@@ -73,18 +95,13 @@ pub fn steps_system(
         match current_state {
             Requested => {
                 // Begin at the beginning
-                let step_ent = steps_action.steps[steps_action.active_step];
-                let step_runner = runners.get(step_ent.0).expect("oops");
-                let mut step_state = states.get_mut(step_ent.0).expect("oops");
-                step_runner.0.activate(*actor, step_ent, &mut cmd);
+                let mut step_state = states.get_mut(steps_action.active_ent.0).expect("oops");
                 *step_state = Requested;
                 let mut current_state = states.get_mut(seq_ent).expect("uh oh");
                 *current_state = Executing;
             }
             Executing => {
-                let mut step_state = states
-                    .get_mut(steps_action.steps[steps_action.active_step].0)
-                    .expect("bug");
+                let mut step_state = states.get_mut(steps_action.active_ent.0).expect("bug");
                 match *step_state {
                     Init => {
                         // Request it! This... should not really happen? But just in case I'm missing something... :)
@@ -95,43 +112,33 @@ pub fn steps_system(
                     }
                     Cancelled | Failure => {
                         // Cancel ourselves
-                        let step_ent = steps_action.steps[steps_action.active_step];
                         let step_state = step_state.clone();
-                        let step_runner = runners.get(step_ent.0).expect("oops");
-                        step_runner.0.deactivate(step_ent, &mut cmd);
                         let mut seq_state = states.get_mut(seq_ent).expect("idk");
                         *seq_state = step_state;
+                        cmd.entity(steps_action.active_ent.0).despawn_recursive();
                     }
                     Success if steps_action.active_step == steps_action.steps.len() - 1 => {
                         // We're done! Let's just be successful
-                        let step_ent = steps_action.steps[steps_action.active_step];
                         let step_state = step_state.clone();
-                        let step_runner = runners.get(step_ent.0).expect("oops");
-                        step_runner.0.deactivate(step_ent, &mut cmd);
                         let mut seq_state = states.get_mut(seq_ent).expect("idk");
                         *seq_state = step_state;
+                        cmd.entity(steps_action.active_ent.0).despawn_recursive();
                     }
                     Success => {
                         // Deactivate current step and go to the next step
-                        let step_ent = steps_action.steps[steps_action.active_step];
-                        let step_runner = runners.get(step_ent.0).expect("oops");
-                        step_runner.0.deactivate(step_ent, &mut cmd);
+                        cmd.entity(steps_action.active_ent.0).despawn_recursive();
 
                         steps_action.active_step += 1;
-                        let step_ent = steps_action.steps[steps_action.active_step];
-                        let step_runner = runners.get(step_ent.0).expect("oops");
-                        let mut step_state = states.get_mut(step_ent.0).expect("oops");
-                        step_runner.0.activate(*actor, step_ent, &mut cmd);
+                        let step_builder = steps_action.steps[steps_action.active_step].clone();
+                        let step_ent = step_builder.attach(&mut cmd, *actor);
+                        let mut step_state = states.get_mut(step_ent).expect("oops");
                         *step_state = ActionState::Requested;
                     }
                 }
             }
             Cancelled => {
                 // Cancel current action
-                let step_ent = steps_action.steps[steps_action.active_step];
-                let step_runner = runners.get(step_ent.0).expect("oops");
-                let mut step_state = states.get_mut(step_ent.0).expect("oops");
-                step_runner.0.activate(*actor, step_ent, &mut cmd);
+                let mut step_state = states.get_mut(steps_action.active_ent.0).expect("oops");
                 *step_state = ActionState::Cancelled;
             }
             Init | Success | Failure => {
@@ -140,51 +147,3 @@ pub fn steps_system(
         }
     }
 }
-
-mod seq_action {
-    use super::*;
-    use serde::Deserialize;
-
-    #[derive(Debug, Deserialize)]
-    struct Steps {
-        steps: Vec<Box<dyn Action>>,
-    }
-
-    #[typetag::deserialize]
-    impl Action for Steps {
-        fn build(
-            self: Box<Self>,
-            actor: Entity,
-            _action_ent: ActionEnt,
-            cmd: &mut Commands,
-        ) -> Box<dyn ActionRunner> {
-            let runner = StepsRunner {
-                steps: self
-                    .steps
-                    .into_iter()
-                    .map(|builder| ActionState::build(builder, actor, cmd))
-                    .collect(),
-            };
-            let children: Vec<_> = runner.steps.iter().map(|x| x.0).collect();
-            cmd.entity(actor).push_children(&children[..]);
-            Box::new(runner)
-        }
-    }
-
-    #[derive(Debug)]
-    struct StepsRunner {
-        steps: Vec<ActionEnt>,
-    }
-
-    impl ActionRunner for StepsRunner {
-        fn activate(&self, _actor: Entity, action_ent: ActionEnt, cmd: &mut Commands) {
-            cmd.entity(action_ent.0).insert(super::Steps {
-                active_step: 0,
-                steps: self.steps.clone(),
-            });
-        }
-        fn deactivate(&self, action_ent: ActionEnt, cmd: &mut Commands) {
-            cmd.entity(action_ent.0).remove::<super::Steps>();
-        }
-    }
-}
diff --git a/src/choices.rs b/src/choices.rs
index bdc756f73aa56528648168b5480c3f88d1cad951..205896457e0dc786621768c4a9da3d71c20b7111 100644
--- a/src/choices.rs
+++ b/src/choices.rs
@@ -1,35 +1,45 @@
+use std::sync::Arc;
+
 use bevy::prelude::*;
-use serde::Deserialize;
 
 use crate::{
-    actions::{Action, ActionState},
-    scorers::{Scorer, Score},
-    thinker::{ActionEnt, ScorerEnt},
+    actions::{ActionBuilder, ActionBuilderWrapper},
+    scorers::{Score, ScorerBuilder},
+    thinker::ScorerEnt,
 };
 
 // Contains different types of Considerations and Actions
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Choice {
     pub scorer: ScorerEnt,
-    pub action_state: ActionEnt,
+    pub action: ActionBuilderWrapper,
 }
 impl Choice {
     pub fn calculate(&self, scores: &Query<&Score>) -> f32 {
-        scores.get(self.scorer.0).expect("Where did the score go?").0
+        scores
+            .get(self.scorer.0)
+            .expect("Where did the score go?")
+            .0
     }
 }
 
-#[derive(Debug, Deserialize)]
+#[derive(Debug)]
 pub struct ChoiceBuilder {
-    pub when: Box<dyn Scorer>,
-    pub then: Box<dyn Action>,
+    pub when: Arc<dyn ScorerBuilder>,
+    pub then: Arc<dyn ActionBuilder>,
 }
 impl ChoiceBuilder {
-    pub fn build(self, actor: Entity, cmd: &mut Commands) -> Choice {
-        let action = self.then;
+    pub fn new(scorer: Arc<dyn ScorerBuilder>, action: Arc<dyn ActionBuilder>) -> Self {
+        Self {
+            when: scorer,
+            then: action,
+        }
+    }
+
+    pub fn build(&self, cmd: &mut Commands, actor: Entity) -> Choice {
         Choice {
-            scorer: self.when.build(actor, cmd),
-            action_state: ActionState::build(action, actor, cmd),
+            scorer: ScorerEnt(self.when.attach(cmd, actor)),
+            action: ActionBuilderWrapper::new(self.then.clone()),
         }
     }
 }
diff --git a/src/evaluators.rs b/src/evaluators.rs
index 34711ce41dabce316e1f8a846cfc8c39fcfea225..e7d92c32f1971dbf9c51f3c17eae4dbc99cf8a76 100644
--- a/src/evaluators.rs
+++ b/src/evaluators.rs
@@ -1,11 +1,8 @@
-use serde::{Deserialize, Serialize};
-
-#[typetag::serde]
 pub trait Evaluator: std::fmt::Debug + Sync + Send {
     fn evaluate(&self, value: f32) -> f32;
 }
 
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug)]
 pub struct LinearEvaluator {
     xa: f32,
     ya: f32,
@@ -36,14 +33,13 @@ impl Default for LinearEvaluator {
     }
 }
 
-#[typetag::serde]
 impl Evaluator for LinearEvaluator {
     fn evaluate(&self, value: f32) -> f32 {
         clamp(self.ya + self.dy_over_dx * (value - self.xa), self.ya, self.yb)
     }
 }
 
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug)]
 pub struct PowerEvaluator {
     xa: f32,
     ya: f32,
@@ -76,7 +72,6 @@ impl Default for PowerEvaluator {
     }
 }
 
-#[typetag::serde]
 impl Evaluator for PowerEvaluator {
     fn evaluate(&self, value: f32) -> f32 {
         let cx = clamp(value, self.xa, self.xb);
@@ -84,7 +79,7 @@ impl Evaluator for PowerEvaluator {
     }
 }
 
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug)]
 pub struct SigmoidEvaluator {
     xa: f32,
     xb: f32,
@@ -124,7 +119,6 @@ impl SigmoidEvaluator {
     }
 }
 
-#[typetag::serde]
 impl Evaluator for SigmoidEvaluator {
     fn evaluate(&self, x: f32) -> f32 {
         let cx_minus_x_mean = clamp(x, self.xa, self.xb) - self.x_mean;
diff --git a/src/lib.rs b/src/lib.rs
index 22e33379a7cc59f84e49c5077ace13b622bf8487..c7b7de97ec0f8313e8cfe8dce18c2232022a28a9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,15 +1,13 @@
 pub use bevy;
-pub use big_brain_derive::*;
-pub use serde;
-pub use typetag;
 
 pub use actions::*;
 pub use choices::*;
 pub use scorers::*;
 pub use thinker::*;
+pub use pickers::*;
 
 pub mod evaluators;
-pub mod pickers;
+mod pickers;
 
 mod actions;
 mod choices;
diff --git a/src/pickers.rs b/src/pickers.rs
index 468e7a14e96f2ffcdefa1871eed96650ec8fb9f4..0ac71e3687b22a3bec4e521d664e0402ec49e367 100644
--- a/src/pickers.rs
+++ b/src/pickers.rs
@@ -1,26 +1,22 @@
 use bevy::prelude::*;
 
-use serde::{Deserialize, Serialize};
+use crate::{choices::Choice, scorers::Score};
 
-use crate::{choices::Choice, scorers::Score, thinker::ActionEnt};
-
-#[typetag::serde]
 pub trait Picker: std::fmt::Debug + Sync + Send {
-    fn pick(&self, _choices: &[Choice], _utilities: &Query<&Score>) -> Option<ActionEnt>;
+    fn pick(&self, _choices: &[Choice], _utilities: &Query<&Score>) -> Option<Choice>;
 }
 
-#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[derive(Debug, Clone, Default)]
 pub struct FirstToScore {
     pub threshold: f32,
 }
 
-#[typetag::serde]
 impl Picker for FirstToScore {
-    fn pick(&self, choices: &[Choice], utilities: &Query<&Score>) -> Option<ActionEnt> {
+    fn pick(&self, choices: &[Choice], utilities: &Query<&Score>) -> Option<Choice> {
         for choice in choices {
             let value = choice.calculate(utilities);
             if value >= self.threshold {
-                return Some(choice.action_state);
+                return Some(choice.clone());
             }
         }
         None
diff --git a/src/scorers.rs b/src/scorers.rs
index 0df09596b6cd976ceed21fec704c19b337a029af..19ee5356ec44e2602458b6aec62746a0a580c8a9 100644
--- a/src/scorers.rs
+++ b/src/scorers.rs
@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
 use bevy::prelude::*;
 
 use crate::ScorerEnt;
@@ -17,41 +19,38 @@ impl Score {
     }
 }
 
-/**
-This trait defines new Scorers. In general, you should use the [derive macro](derive.Scorer.html) instead.
-*/
-#[typetag::deserialize]
-pub trait Scorer: std::fmt::Debug + Sync + Send {
-    fn build(&self, entity: Entity, cmd: &mut Commands) -> ScorerEnt;
+pub trait ScorerBuilder: std::fmt::Debug + Sync + Send {
+    fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity);
+    fn attach(&self, cmd: &mut Commands, actor: Entity) -> Entity {
+        let scorer_ent = cmd.spawn().id();
+        cmd.entity(scorer_ent).insert(Score::default());
+        cmd.entity(actor).push_children(&[scorer_ent]);
+        self.build(cmd, scorer_ent, actor);
+        scorer_ent
+    }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct FixedScore(f32);
 
+impl FixedScore {
+    pub fn build(score: f32) -> FixedScoreBuilder {
+        FixedScoreBuilder(score)
+    }
+}
+
 pub fn fixed_score_system(mut query: Query<(&FixedScore, &mut Score)>) {
     for (FixedScore(fixed), mut score) in query.iter_mut() {
         score.set(*fixed);
     }
 }
 
-mod fixed_score {
-    use super::*;
-
-    use serde::Deserialize;
-
-    #[derive(Debug, Deserialize)]
-    struct FixedScore(f32);
+#[derive(Debug, Clone)]
+pub struct FixedScoreBuilder(f32);
 
-    #[typetag::deserialize]
-    impl Scorer for FixedScore {
-        fn build(&self, actor: Entity, cmd: &mut Commands) -> ScorerEnt {
-            let ent = ScorerEnt(cmd.spawn().id());
-            cmd.entity(ent.0)
-                .insert(Score::default())
-                .insert(super::FixedScore(self.0));
-            cmd.entity(actor).push_children(&[ent.0]);
-            ent
-        }
+impl ScorerBuilder for FixedScoreBuilder {
+    fn build(&self, cmd: &mut Commands, action: Entity, _actor: Entity) {
+        cmd.entity(action).insert(FixedScore(self.0));
     }
 }
 
@@ -61,6 +60,15 @@ pub struct AllOrNothing {
     scorers: Vec<ScorerEnt>,
 }
 
+impl AllOrNothing {
+    pub fn build(threshold: f32) -> AllOrNothingBuilder {
+        AllOrNothingBuilder {
+            threshold,
+            scorers: Vec::new(),
+        }
+    }
+}
+
 pub fn all_or_nothing_system(query: Query<(Entity, &AllOrNothing)>, mut scores: Query<&mut Score>) {
     for (
         aon_ent,
@@ -84,36 +92,32 @@ pub fn all_or_nothing_system(query: Query<(Entity, &AllOrNothing)>, mut scores:
         score.set(crate::evaluators::clamp(sum, 0.0, 100.0));
     }
 }
+#[derive(Debug, Clone)]
+pub struct AllOrNothingBuilder {
+    threshold: f32,
+    scorers: Vec<Arc<dyn ScorerBuilder>>,
+}
 
-mod all_or_nothing {
-    use super::*;
-
-    use serde::Deserialize;
-
-    #[derive(Debug, Deserialize)]
-    struct AllOrNothing {
-        threshold: f32,
-        scorers: Vec<Box<dyn Scorer>>,
+impl AllOrNothingBuilder {
+    pub fn when(&mut self, scorer: impl ScorerBuilder + 'static) -> &mut Self {
+        self.scorers.push(Arc::new(scorer));
+        self
     }
+}
 
-    #[typetag::deserialize]
-    impl Scorer for AllOrNothing {
-        fn build(&self, actor: Entity, cmd: &mut Commands) -> ScorerEnt {
-            let ent = ScorerEnt(cmd.spawn().id());
-            let scorers: Vec<_> = self
-                .scorers
-                .iter()
-                .map(|scorer| scorer.build(actor, cmd).0)
-                .collect();
-            cmd.entity(ent.0)
-                .insert(Score::default())
-                .insert(super::AllOrNothing {
-                    threshold: self.threshold,
-                    scorers: scorers.into_iter().map(ScorerEnt).collect(),
-                });
-            cmd.entity(actor).push_children(&[ent.0]);
-            ent
-        }
+impl ScorerBuilder for AllOrNothingBuilder {
+    fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
+        let scorers: Vec<_> = self
+            .scorers
+            .iter()
+            .map(|scorer| scorer.attach(cmd, actor))
+            .collect();
+        cmd.entity(scorer)
+            .insert(Score::default())
+            .insert(super::AllOrNothing {
+                threshold: self.threshold,
+                scorers: scorers.into_iter().map(ScorerEnt).collect(),
+            });
     }
 }
 
@@ -123,6 +127,15 @@ pub struct SumOfScorers {
     scorers: Vec<ScorerEnt>,
 }
 
+impl SumOfScorers {
+    pub fn build(threshold: f32) -> SumOfScorersBuilder {
+        SumOfScorersBuilder {
+            threshold,
+            scorers: Vec::new(),
+        }
+    }
+}
+
 pub fn sum_of_scorers_system(query: Query<(Entity, &SumOfScorers)>, mut scores: Query<&mut Score>) {
     for (
         sos_ent,
@@ -145,34 +158,29 @@ pub fn sum_of_scorers_system(query: Query<(Entity, &SumOfScorers)>, mut scores:
     }
 }
 
-mod sum_of_scorers {
-    use super::*;
-
-    use serde::Deserialize;
+#[derive(Debug, Clone)]
+pub struct SumOfScorersBuilder {
+    threshold: f32,
+    scorers: Vec<Arc<dyn ScorerBuilder>>,
+}
 
-    #[derive(Debug, Deserialize)]
-    struct SumOfScorers {
-        threshold: f32,
-        scorers: Vec<Box<dyn Scorer>>,
+impl SumOfScorersBuilder {
+    pub fn when(&mut self, scorer: impl ScorerBuilder + 'static) -> &mut Self {
+        self.scorers.push(Arc::new(scorer));
+        self
     }
+}
 
-    #[typetag::deserialize]
-    impl Scorer for SumOfScorers {
-        fn build(&self, actor: Entity, cmd: &mut Commands) -> ScorerEnt {
-            let ent = ScorerEnt(cmd.spawn().id());
-            let scorers: Vec<_> = self
-                .scorers
-                .iter()
-                .map(|scorer| scorer.build(actor, cmd).0)
-                .collect();
-            cmd.entity(ent.0)
-                .insert(Score::default())
-                .insert(super::AllOrNothing {
-                    threshold: self.threshold,
-                    scorers: scorers.into_iter().map(ScorerEnt).collect(),
-                });
-            cmd.entity(actor).push_children(&[ent.0]);
-            ent
-        }
+impl ScorerBuilder for SumOfScorersBuilder {
+    fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
+        let scorers: Vec<_> = self
+            .scorers
+            .iter()
+            .map(|scorer| scorer.attach(cmd, actor))
+            .collect();
+        cmd.entity(scorer).insert(AllOrNothing {
+            threshold: self.threshold,
+            scorers: scorers.into_iter().map(ScorerEnt).collect(),
+        });
     }
 }
diff --git a/src/thinker.rs b/src/thinker.rs
index dc095d062723acd6b535c2336229d23805f85c90..442c2026725a2157aaa5414e1c196d7ef36043be 100644
--- a/src/thinker.rs
+++ b/src/thinker.rs
@@ -1,15 +1,15 @@
-use std::fs::File;
-use std::path::Path;
-use std::time::{Duration, Instant};
+use std::{
+    sync::Arc,
+    time::{Duration, Instant},
+};
 
 use bevy::prelude::*;
-use serde::Deserialize;
 
 use crate::{
-    actions::{self, Action, ActionRunner, ActionRunnerWrapper, ActionState},
+    actions::{self, ActionBuilder, ActionBuilderWrapper, ActionState},
     choices::{Choice, ChoiceBuilder},
-    scorers::Score,
     pickers::Picker,
+    scorers::{Score, ScorerBuilder},
 };
 
 #[derive(Debug, Clone, Copy)]
@@ -20,87 +20,81 @@ pub struct ScorerEnt(pub Entity);
 
 #[derive(Debug)]
 pub struct Thinker {
-    pub picker: Box<dyn Picker>,
-    pub otherwise: Option<ActionEnt>,
-    pub choices: Vec<Choice>,
-    pub current_action: Option<ActionEnt>,
+    picker: Arc<dyn Picker>,
+    otherwise: Option<ActionBuilderWrapper>,
+    choices: Vec<Choice>,
+    current_action: Option<(ActionEnt, ActionBuilderWrapper)>,
 }
 
 impl Thinker {
-    pub fn load_from_str<S: AsRef<str>>(string: S) -> builder::Thinker {
-        ron::de::from_str(string.as_ref()).expect("Failed to parse RON")
+    pub fn build() -> ThinkerBuilder {
+        ThinkerBuilder::new()
     }
+}
 
-    pub fn load_from_path<P: AsRef<Path>>(path: P) -> builder::Thinker {
-        let f = File::open(&path).expect("Failed to open file");
-        ron::de::from_reader(f).expect("Failed to read .ron file")
-    }
+#[derive(Debug, Default)]
+pub struct ThinkerBuilder {
+    pub picker: Option<Arc<dyn Picker>>,
+    pub otherwise: Option<ActionBuilderWrapper>, // Arc<dyn ActionBuilder>?
+    pub choices: Vec<ChoiceBuilder>,
 }
 
-mod builder {
-    use super::*;
-    #[derive(Debug, Deserialize)]
-    pub struct Thinker {
-        pub picker: Box<dyn Picker>,
-        pub otherwise: Option<Box<dyn Action>>,
-        pub choices: Vec<ChoiceBuilder>,
+impl ThinkerBuilder {
+    pub(crate) fn new() -> Self {
+        Self {
+            picker: None,
+            otherwise: None,
+            choices: Vec::new(),
+        }
     }
-}
 
-impl builder::Thinker {
-    pub fn build(self, actor: Entity, cmd: &mut Commands) -> ActionEnt {
-        let action_ent = ActionState::build(Box::new(self), actor, cmd);
-        cmd.entity(action_ent.0)
-            .insert(ActiveThinker(true))
-            .insert(ActionState::Requested);
-        action_ent
+    pub fn picker(&mut self, picker: impl Picker + 'static) -> &mut Self {
+        self.picker = Some(Arc::new(picker));
+        self
+    }
+
+    pub fn when(
+        &mut self,
+        scorer: impl ScorerBuilder + 'static,
+        action: impl ActionBuilder + 'static,
+    ) -> &mut Self {
+        self.choices.push(ChoiceBuilder::new(Arc::new(scorer), Arc::new(action)));
+        self
+    }
+
+    pub fn otherwise(&mut self, otherwise: impl ActionBuilder + 'static) -> &mut Self {
+        self.otherwise = Some(ActionBuilderWrapper::new(Arc::new(otherwise)));
+        self
     }
 }
 
-#[typetag::deserialize]
-impl Action for builder::Thinker {
-    fn build(
-        self: Box<Self>,
-        actor: Entity,
-        action_ent: ActionEnt,
-        cmd: &mut Commands,
-    ) -> Box<dyn ActionRunner> {
+impl ActionBuilder for ThinkerBuilder {
+    fn build(&self, cmd: &mut Commands, action_ent: Entity, actor: Entity) {
+        println!("building thinker");
         let choices = self
             .choices
-            .into_iter()
-            .map(|choice| choice.build(actor, cmd))
+            .iter()
+            .map(|choice| choice.build(cmd, actor))
             .collect();
-        let otherwise = self
-            .otherwise
-            .map(|builder| ActionState::build(builder, actor, cmd));
-        cmd.entity(action_ent.0).insert(Thinker {
-            picker: self.picker,
-            choices,
-            otherwise,
-            current_action: None,
-        });
-        cmd.entity(actor).push_children(&[action_ent.0]);
-        Box::new(ThinkerRunner)
+        cmd.entity(action_ent)
+            .insert(Thinker {
+                // TODO: reasonable default?...
+                picker: self
+                    .picker
+                    .clone()
+                    .expect("ThinkerBuilder must have a Picker"),
+                choices,
+                otherwise: self.otherwise.clone(),
+                current_action: None,
+            })
+            .insert(ActiveThinker(false))
+            .insert(ActionState::Requested);
     }
 }
 
 #[derive(Debug)]
 pub struct ActiveThinker(bool);
 
-#[derive(Debug)]
-pub struct ThinkerRunner;
-
-impl ActionRunner for ThinkerRunner {
-    fn activate(&self, _: Entity, action_ent: ActionEnt, cmd: &mut Commands) {
-        cmd.entity(action_ent.0)
-            .insert(ActiveThinker(false))
-            .insert(ActionState::Requested);
-    }
-    fn deactivate(&self, action_ent: ActionEnt, cmd: &mut Commands) {
-        cmd.entity(action_ent.0).remove::<ActiveThinker>();
-    }
-}
-
 pub struct ThinkerIterations {
     index: usize,
     max_duration: Duration,
@@ -125,10 +119,11 @@ pub fn thinker_system(
     mut thinker_q: Query<(Entity, &Parent, &mut Thinker, &ActiveThinker)>,
     utilities: Query<&Score>,
     mut action_states: Query<&mut actions::ActionState>,
-    builder_wrappers: Query<&ActionRunnerWrapper>,
 ) {
     let start = Instant::now();
-    for (thinker_ent, Parent(actor), mut thinker, active_thinker) in thinker_q.iter_mut().skip(iterations.index) {
+    for (thinker_ent, Parent(actor), mut thinker, active_thinker) in
+        thinker_q.iter_mut().skip(iterations.index)
+    {
         iterations.index += 1;
 
         let thinker_state = action_states
@@ -144,24 +139,24 @@ pub fn thinker_system(
             }
             ActionState::Cancelled => {
                 if let Some(current) = &mut thinker.current_action {
-                    let state = action_states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.").clone();
+                    let state = action_states.get_mut(current.0.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.").clone();
                     match state {
                         ActionState::Success | ActionState::Failure => {
                             let mut act_state = action_states.get_mut(thinker_ent).expect("???");
                             *act_state = state.clone();
-                            let mut state = action_states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
+                            let mut state = action_states.get_mut(current.0.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
                             *state = ActionState::Init;
                             thinker.current_action = None;
                         }
                         _ => {
-                            let mut state = action_states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
+                            let mut state = action_states.get_mut(current.0.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
                             *state = ActionState::Cancelled;
                         }
                     }
                 }
             }
             ActionState::Requested | ActionState::Executing => {
-                if let Some(picked_action_ent) = thinker.picker.pick(&thinker.choices, &utilities) {
+                if let Some(choice) = thinker.picker.pick(&thinker.choices, &utilities) {
                     // Think about what action we're supposed to be taking. We do this
                     // every tick, because we might change our mind.
                     // ...and then execute it (details below).
@@ -170,13 +165,12 @@ pub fn thinker_system(
                         thinker_ent,
                         *actor,
                         &mut thinker,
-                        &picked_action_ent,
+                        &choice.action,
                         &mut action_states,
-                        &builder_wrappers,
                     );
                 } else if let Some(default_action_ent) = &thinker.otherwise {
                     // Otherwise, let's just execute the default one! (if it's there)
-                    let default_action_ent = *default_action_ent;
+                    let default_action_ent = default_action_ent.clone();
                     exec_picked_action(
                         &mut cmd,
                         thinker_ent,
@@ -184,21 +178,18 @@ pub fn thinker_system(
                         &mut thinker,
                         &default_action_ent,
                         &mut action_states,
-                        &builder_wrappers,
                     );
                 } else if let Some(current) = &mut thinker.current_action {
                     // If we didn't pick anything, and there's no default action,
                     // we need to see if there's any action currently executing,
                     // and cancel it. We also use this opportunity to clean up
                     // stale action components so they don't slow down joins.
-                    let mut state = action_states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
-                    let factory = builder_wrappers.get(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
+                    let mut state = action_states.get_mut(current.0.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
                     match *state {
                         actions::ActionState::Init
                         | actions::ActionState::Success
                         | actions::ActionState::Failure => {
-                            factory.0.deactivate(*current, &mut cmd);
-                            *state = ActionState::Init;
+                            cmd.entity(current.0 .0).despawn_recursive();
                             thinker.current_action = None;
                         }
                         _ => {
@@ -220,9 +211,8 @@ fn exec_picked_action(
     thinker_ent: Entity,
     actor: Entity,
     thinker: &mut Mut<Thinker>,
-    picked_action_ent: &ActionEnt,
+    picked_action: &ActionBuilderWrapper,
     states: &mut Query<&mut ActionState>,
-    builder_wrappers: &Query<&ActionRunnerWrapper>,
 ) {
     // If we do find one, then we need to grab the corresponding
     // component for it. The "action" that `picker.pick()` returns
@@ -235,12 +225,12 @@ fn exec_picked_action(
     // (maybe not in this logic), but we do need some kind of
     // oscillation protection so we're not just bouncing back and
     // forth between the same couple of actions.
-    if let Some(current) = &mut thinker.current_action {
-        if current.0 != picked_action_ent.0 {
+    if let Some((action_ent, ActionBuilderWrapper(current_id, _))) = &mut thinker.current_action {
+        if *current_id != picked_action.0 {
             // So we've picked a different action than we were
             // currently executing. Just like before, we grab the
             // actual Action component (and we assume it exists).
-            let mut curr_action_state = states.get_mut(current.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
+            let mut curr_action_state = states.get_mut(action_ent.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
             // If the action is executing, or was requested, we
             // need to cancel it to make sure it stops. The Action
             // system will take care of resetting its state as
@@ -252,13 +242,11 @@ fn exec_picked_action(
                     *thinker_state = ActionState::Cancelled;
                 }
                 ActionState::Init | ActionState::Success | ActionState::Failure => {
-                    let current_action_factory = builder_wrappers.get(current.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
-                    current_action_factory
-                        .0
-                        .deactivate(*picked_action_ent, cmd);
                     let old_state = curr_action_state.clone();
-                    *curr_action_state = ActionState::Init;
-                    *current = *picked_action_ent;
+                    thinker.current_action = Some((
+                        ActionEnt(picked_action.1.attach(cmd, actor)),
+                        picked_action.clone(),
+                    ));
                     let mut thinker_state = states.get_mut(thinker_ent).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
                     *thinker_state = old_state;
                 }
@@ -270,13 +258,9 @@ fn exec_picked_action(
             // it as Requested if for some reason it had finished
             // but the Action System hasn't gotten around to
             // cleaning it up.
-            let mut picked_action_state = states.get_mut(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
-            if *picked_action_state == ActionState::Init {
-                let picked_action_factory = builder_wrappers.get(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
-                picked_action_factory
-                    .0
-                    .activate(actor, *picked_action_ent, cmd);
-                *picked_action_state = ActionState::Requested;
+            let mut curr_action_state = states.get_mut(action_ent.0).expect("Couldn't find a component corresponding to the current action. This is definitely a bug.");
+            if *curr_action_state == ActionState::Init {
+                *curr_action_state = ActionState::Requested;
             }
         }
     } else {
@@ -284,12 +268,7 @@ fn exec_picked_action(
         // current_action in the thinker. The logic here is pretty
         // straightforward -- we set the action, Request it, and
         // that's it.
-        let picked_action_factory = builder_wrappers.get(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
-        let mut picked_action_state = states.get_mut(picked_action_ent.0).expect("Couldn't find an Action component corresponding to an Action entity. This is definitely a bug.");
-        picked_action_factory
-            .0
-            .activate(actor, *picked_action_ent, cmd);
-        thinker.current_action = Some(*picked_action_ent);
-        *picked_action_state = ActionState::Requested;
+        let new_action = picked_action.1.attach(cmd, actor);
+        thinker.current_action = Some((ActionEnt(new_action), picked_action.clone()));
     }
 }