diff --git a/big-brain-derive/src/action.rs b/big-brain-derive/src/action.rs
index e023bdd8a8ac126a048ebc62c8ace6a441905547..e03bd477b246204695efb9c6b775aeeadce86740 100644
--- a/big-brain-derive/src/action.rs
+++ b/big-brain-derive/src/action.rs
@@ -72,7 +72,7 @@ impl ToTokens for Action {
             mod big_brain_action_builder {
                 use super::#ident as Comp;
 
-                use big_brain::{typetag, serde::Deserialize, Action, ActionManager, bevy::prelude::{Entity, Commands}, ActionEnt};
+                use big_brain::{typetag, serde::Deserialize, Action, ActionRunner, bevy::prelude::{Entity, Commands}, ActionEnt};
 
                 #[derive(Debug, Deserialize)]
                 struct #ident {
@@ -81,12 +81,12 @@ impl ToTokens for Action {
 
                 #[typetag::deserialize]
                 impl Action for #ident {
-                    fn build(self: Box<Self>, actor: Entity, action_ent: ActionEnt, cmd: &mut Commands) -> Box<dyn ActionManager> {
+                    fn build(self: Box<Self>, actor: Entity, action_ent: ActionEnt, cmd: &mut Commands) -> Box<dyn ActionRunner> {
                         self
                     }
                 }
 
-                impl ActionManager for #ident {
+                impl ActionRunner for #ident {
                     fn activate(&self, actor: Entity, action_ent: ActionEnt, cmd: &mut Commands) {
                         cmd.entity(action_ent.0).insert(Comp {
                             #(#field_assignments),*
diff --git a/src/actions.rs b/src/actions.rs
index 2096209ae7670ea39d24c0ee620d41a82fdf826e..6b8b2a7e7810f9532fc539b8a9071acc4a3b19c3 100644
--- a/src/actions.rs
+++ b/src/actions.rs
@@ -3,7 +3,7 @@ use bevy::prelude::*;
 use crate::ActionEnt;
 
 #[derive(Debug)]
-pub struct ActionManagerWrapper(pub(crate) Box<dyn ActionManager>);
+pub struct ActionRunnerWrapper(pub(crate) Box<dyn ActionRunner>);
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum ActionState {
@@ -22,7 +22,7 @@ impl ActionState {
 
     pub(crate) fn build(builder: Box<dyn Action>, actor: Entity, cmd: &mut Commands) -> ActionEnt {
         let action_ent = ActionEnt(cmd.spawn().id());
-        let manager_wrapper = ActionManagerWrapper(builder.build(actor, action_ent, cmd));
+        let manager_wrapper = ActionRunnerWrapper(builder.build(actor, action_ent, cmd));
         cmd.entity(action_ent.0)
             .insert(ActionState::default())
             .insert(manager_wrapper);
@@ -47,10 +47,144 @@ pub trait Action: std::fmt::Debug + Send + Sync {
         actor: Entity,
         action_ent: ActionEnt,
         cmd: &mut Commands,
-    ) -> Box<dyn ActionManager>;
+    ) -> Box<dyn ActionRunner>;
 }
 
-pub trait ActionManager: std::fmt::Debug + Send + Sync {
+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);
 }
+
+#[derive(Debug)]
+pub struct Steps {
+    steps: Vec<ActionEnt>,
+    active_step: usize,
+}
+
+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() {
+        let current_state = states.get_mut(seq_ent).expect("uh oh").clone();
+        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);
+                *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");
+                match *step_state {
+                    Init => {
+                        // Request it! This... should not really happen? But just in case I'm missing something... :)
+                        *step_state = Requested;
+                    }
+                    Executing | Requested => {
+                        // do nothing. Everything's running as it should.
+                    }
+                    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;
+                    }
+                    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;
+                    }
+                    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);
+
+                        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);
+                        *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);
+                *step_state = ActionState::Cancelled;
+            }
+            Init | Success | Failure => {
+                // Do nothing.
+            }
+        }
+    }
+}
+
+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/lib.rs b/src/lib.rs
index ddb48e8dc3bcfbb0ef37b629277eb4e0ffbcc0d3..22e33379a7cc59f84e49c5077ace13b622bf8487 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -23,6 +23,7 @@ pub struct BigBrainPlugin;
 impl Plugin for BigBrainPlugin {
     fn build(&self, app: &mut AppBuilder) {
         app.add_system(thinker_system.system());
+        app.add_system(steps_system.system());
         app.add_system(fixed_score_system.system());
         app.add_system(all_or_nothing_system.system());
         app.add_system(sum_of_scorers_system.system());
diff --git a/src/thinker.rs b/src/thinker.rs
index d4ef4ddc9be35d953255d7385d97815e80e95cb5..dc095d062723acd6b535c2336229d23805f85c90 100644
--- a/src/thinker.rs
+++ b/src/thinker.rs
@@ -6,7 +6,7 @@ use bevy::prelude::*;
 use serde::Deserialize;
 
 use crate::{
-    actions::{self, Action, ActionManager, ActionManagerWrapper, ActionState},
+    actions::{self, Action, ActionRunner, ActionRunnerWrapper, ActionState},
     choices::{Choice, ChoiceBuilder},
     scorers::Score,
     pickers::Picker,
@@ -64,7 +64,7 @@ impl Action for builder::Thinker {
         actor: Entity,
         action_ent: ActionEnt,
         cmd: &mut Commands,
-    ) -> Box<dyn ActionManager> {
+    ) -> Box<dyn ActionRunner> {
         let choices = self
             .choices
             .into_iter()
@@ -80,7 +80,7 @@ impl Action for builder::Thinker {
             current_action: None,
         });
         cmd.entity(actor).push_children(&[action_ent.0]);
-        Box::new(ThinkerManager)
+        Box::new(ThinkerRunner)
     }
 }
 
@@ -88,9 +88,9 @@ impl Action for builder::Thinker {
 pub struct ActiveThinker(bool);
 
 #[derive(Debug)]
-pub struct ThinkerManager;
+pub struct ThinkerRunner;
 
-impl ActionManager for ThinkerManager {
+impl ActionRunner for ThinkerRunner {
     fn activate(&self, _: Entity, action_ent: ActionEnt, cmd: &mut Commands) {
         cmd.entity(action_ent.0)
             .insert(ActiveThinker(false))
@@ -125,7 +125,7 @@ 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<&ActionManagerWrapper>,
+    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) {
@@ -222,7 +222,7 @@ fn exec_picked_action(
     thinker: &mut Mut<Thinker>,
     picked_action_ent: &ActionEnt,
     states: &mut Query<&mut ActionState>,
-    builder_wrappers: &Query<&ActionManagerWrapper>,
+    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