diff --git a/README.md b/README.md
index 69eeb4aca..3901cc15d 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,7 @@ CLI11 is a command line parser for C++11 and beyond that provides a rich feature
     -   [Subcommands](#subcommands)
         -   [Subcommand options](#subcommand-options)
         -   [Option groups](#option-groups) 🚧
+        -   [Callbacks](#callbacks)
     -   [Configuration file](#configuration-file)
     -   [Inheriting defaults](#inheriting-defaults)
     -   [Formatting](#formatting)
@@ -235,7 +236,7 @@ Option_group *app.add_option_group(name,description); // 🚧
 -app.add_mutable_set_ignore_case_underscore(... // πŸ†• String only
 ```
 
-An option name must start with a alphabetic character, underscore, or a number. For long options, after the first character '.', and '-' are also valid. For the `add_flag*` functions '{' has special meaning. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option`.
+An option name must start with a alphabetic character, underscore, or a number🚧. For long options, after the first character '.', and '-' are also valid. For the `add_flag*` functions '{' has special meaning. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on help line for its positional form. If you want the default value to print in the help description, pass in `true` for the final parameter for `add_option`.
 
 The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed.  The type can be any type supported by the `add_option` function.
 
@@ -275,7 +276,7 @@ The add commands return a pointer to an internally stored `Option`. If you set t
 Before parsing, you can set the following options:
 
 -   `->required()`: The program will quit if this option is not present. This is `mandatory` in Plumbum, but required options seems to be a more standard term. For compatibility, `->mandatory()` also works.
--   `->expected(N)`: Take `N` values instead of as many as possible, only for vector args. If negative, require at least `-N`; end with `--` or another recognized option.
+-   `->expected(N)`: Take `N` values instead of as many as possible, only for vector args. If negative, require at least `-N`; end with `--` or another recognized option or subcommand.
 -   `->type_name(typename)`: Set the name of an Option's type (`type_name_fn` allows a function instead)
 -   `->type_size(N)`: Set the intrinsic size of an option. The parser will require multiples of this number if negative.
 -   `->needs(opt)`: This option requires another option to also be present, opt is an `Option` pointer.
@@ -353,7 +354,7 @@ These Validators can be used by simply passing the name into the `check` or `tra
 ->check(CLI::Range(0,10));
 ```
 
-Validators can be merged using `&` and `|` and inverted using `!`
+Validators can be merged using `&` and `|` and inverted using `!`🚧
 such as
 ```cpp
 ->check(CLI::Range(0,10)|CLI::Range(20,30));
@@ -492,7 +493,9 @@ There are several options that are supported on the main app and subcommands and
 -   `.count(option_name)`: Returns the number of times a particular option was called
 -   `.count_all()`: 🚧 Returns the total number of arguments a particular subcommand processed, on the master App it returns the total number of processed commands
 -   `.name(name)`: Add or change the name.
--   `.callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point.
+-   `.callback(void() function)`: Set the callback that runs at the end of parsing. The options have already run at this point. See [Subcommand callbacks](#callbacks) for some additional details.
+-   `.immediate_callback()`: 🚧 Specify that the callback for a subcommand should run immediately on completion of a subcommand vs at the completion of all parsing if this option is not used.
+-    `.pre_parse_callback(void(size_t) function)`: 🚧 Set a callback that executes after the first argument of an application is processed.  See [Subcommand callbacks](#callbacks) for some additional details.
 -   `.allow_extras()`: Do not throw an error if extra arguments are left over.
 -   `.positionals_at_end()`: 🚧 Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
 -   `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognized item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app.
@@ -505,9 +508,48 @@ There are several options that are supported on the main app and subcommands and
 
 > Note: if you have a fixed number of required positional options, that will match before subcommand names. `{}` is an empty filter function.
 
+
+#### Callbacks 
+A subcommand has two optional callbacks that are executed at different stages of processing.  The `preparse_callback` 🚧 is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process.  For the main app the first argument is considered the program name,  for subcommands the first argument is the subcommand name.  For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group.
+The second callback is executed after parsing.  Depending on the status of the `immediate_callback` flag 🚧.  This is either immediately after the parsing of the subcommand.  Or if the flag is false, once after parsing of all arguments.  If the immediate_callback is set then the callback can be executed multiple times.   If the main app or subcommand has a config file, no data from the config file will be reflected in immediate_callback.
+
+For example say an application was set up like
+```cpp
+app.callback(ac);
+sub1=app.add_subcommand("sub1")->callback(c1)->preparse_callback(pc1)->immediate_callback();
+sub2=app.add_subcommand("sub2")->callback(c2)->preparse_callback(pc2);
+app.preparse_callback( pa1);
+
+... A bunch of other options
+
+```
+Then the command line is given as
+
+```
+program --opt1 opt1_val  sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub1opt2 sub2 --sub2opt2 val
+```
+
+* pa will be called prior to parsing any values with an argument of 14.
+* pc1 will be called immediately after processing the sub1 command with a value of 10.
+* c1 will be called when the `sub2` command is encountered
+* pc2 will be called with value of 6 after the sub2 command is encountered.
+* c1 will be called again after the second sub2 command is encountered
+* ac will be called after completing the parse
+* c2 will be called once after processing all arguments 
+
+A subcommand is considered terminated when one of the following conditions are met.
+1. There are no more arguments to process
+2. Another subcommand is encountered that would not fit in an optional slot of the subcommand
+3. The positional_mark(`--`) is encountered and there are no available positional slots in the subcommand.
+4. The subcommand_terminator mark(`++`) is encountered
+
+If the `immediate_callback` flag is set then all contained options are processed and the callback is triggered.  If a subcommand with an immediate_callback flag is called again, then the contained options are reset, and can be triggered again.  
+
+
+
 #### Option groups 🚧
 
-The method 
+The subcommand method 
 ```cpp
 .add_option_group(name,description)
 ```
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index e190861bb..cdafaf1b3 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -94,6 +94,28 @@ add_test(NAME option_groups_extra2 COMMAND option_groups --csv --address "192.16
 set_property(TEST option_groups_extra2 PROPERTY PASS_REGULAR_EXPRESSION
     "at most 1")
 	
+add_cli_exe(positional_arity positional_arity.cpp)
+add_test(NAME positional_arity1 COMMAND positional_arity one )
+set_property(TEST positional_arity1 PROPERTY PASS_REGULAR_EXPRESSION
+    "File 1 = one")
+add_test(NAME positional_arity2 COMMAND positional_arity one two )
+set_property(TEST positional_arity2 PROPERTY PASS_REGULAR_EXPRESSION
+    "File 1 = one"
+    "File 2 = two")
+add_test(NAME positional_arity3 COMMAND positional_arity 1 2 one)
+set_property(TEST positional_arity3 PROPERTY PASS_REGULAR_EXPRESSION
+    "File 1 = one")
+add_test(NAME positional_arity_fail COMMAND positional_arity 1 one two)
+set_property(TEST positional_arity_fail PROPERTY PASS_REGULAR_EXPRESSION
+    "Could not convert")
+
+add_cli_exe(shapes shapes.cpp)
+add_test(NAME shapes_all COMMAND shapes circle 4.4 circle 10.7 rectangle 4 4 circle 2.3 triangle 4.5 ++ rectangle 2.1 ++ circle 234.675)
+set_property(TEST shapes_all PROPERTY PASS_REGULAR_EXPRESSION
+    "circle2"
+    "circle4"
+    "rectangle2 with edges [2.1,2.1]"
+    "triangel1 with sides [4.5]")
 	
 add_cli_exe(ranges ranges.cpp)
 add_test(NAME ranges_range COMMAND ranges --range 1 2 3)
diff --git a/examples/positional_arity.cpp b/examples/positional_arity.cpp
new file mode 100644
index 000000000..0b390df25
--- /dev/null
+++ b/examples/positional_arity.cpp
@@ -0,0 +1,38 @@
+#include "CLI/CLI.hpp"
+
+int main(int argc, char **argv) {
+
+    CLI::App app("test for positional arity");
+
+    auto numbers = app.add_option_group("numbers", "specify key numbers");
+    auto files = app.add_option_group("files", "specify files");
+    int num1 = -1, num2 = -1;
+    numbers->add_option("num1", num1, "first number");
+    numbers->add_option("num2", num2, "second number");
+    std::string file1, file2;
+    files->add_option("file1", file1, "first file")->required();
+    files->add_option("file2", file2, "second file");
+    // set a pre parse callback that turns the numbers group on or off depending on the number of arguments
+    app.preparse_callback([numbers](size_t arity) {
+        if(arity <= 2) {
+            numbers->disabled();
+        } else {
+            numbers->disabled(false);
+        }
+    });
+
+    CLI11_PARSE(app, argc, argv);
+
+    if(num1 != -1)
+        std::cout << "Num1 = " << num1 << '\n';
+
+    if(num2 != -1)
+        std::cout << "Num2 = " << num2 << '\n';
+
+    std::cout << "File 1 = " << file1 << '\n';
+    if(!file2.empty()) {
+        std::cout << "File 2 = " << file2 << '\n';
+    }
+
+    return 0;
+}
diff --git a/examples/shapes.cpp b/examples/shapes.cpp
new file mode 100644
index 000000000..1fcf76961
--- /dev/null
+++ b/examples/shapes.cpp
@@ -0,0 +1,48 @@
+#include "CLI/CLI.hpp"
+
+int main(int argc, char **argv) {
+
+    CLI::App app("load shapes");
+
+    app.set_help_all_flag("--help-all");
+    auto circle = app.add_subcommand("circle", "draw a circle")->immediate_callback();
+    double radius{0.0};
+    int circle_counter = 0;
+    circle->callback([&radius, &circle_counter] {
+        ++circle_counter;
+        std::cout << "circle" << circle_counter << " with radius " << radius << std::endl;
+    });
+
+    circle->add_option("radius", radius, "the radius of the circle")->required();
+
+    auto rect = app.add_subcommand("rectangle", "draw a rectangle")->immediate_callback();
+    double edge1{0.0};
+    double edge2{0.0};
+    int rect_counter = 0;
+    rect->callback([&edge1, &edge2, &rect_counter] {
+        ++rect_counter;
+        if(edge2 == 0) {
+            edge2 = edge1;
+        }
+        std::cout << "rectangle" << rect_counter << " with edges [" << edge1 << ',' << edge2 << "]" << std::endl;
+        edge2 = 0;
+    });
+
+    rect->add_option("edge1", edge1, "the first edge length of the rectangle")->required();
+    rect->add_option("edge2", edge2, "the second edge length of the rectangle");
+
+    auto tri = app.add_subcommand("triangle", "draw a rectangle")->immediate_callback();
+    std::vector<double> sides;
+    int tri_counter = 0;
+    tri->callback([&sides, &tri_counter] {
+        ++tri_counter;
+
+        std::cout << "triangle" << tri_counter << " with sides [" << CLI::detail::join(sides) << "]" << std::endl;
+    });
+
+    tri->add_option("sides", sides, "the side lengths of the triangle");
+
+    CLI11_PARSE(app, argc, argv);
+
+    return 0;
+}
diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp
index e5481f862..01aaa66fc 100644
--- a/include/CLI/App.hpp
+++ b/include/CLI/App.hpp
@@ -38,7 +38,7 @@ namespace CLI {
 #endif
 
 namespace detail {
-enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS, SUBCOMMAND };
+enum class Classifier { NONE, POSITIONAL_MARK, SHORT, LONG, WINDOWS, SUBCOMMAND, SUBCOMMAND_TERMINATOR };
 struct AppFriend;
 } // namespace detail
 
@@ -90,6 +90,16 @@ class App {
     /// If set to true the subcommand is disabled and cannot be used, ignored for main app
     bool disabled_{false};
 
+    /// Flag indicating that the pre_parse_callback has been triggered
+    bool pre_parse_called_{false};
+
+    /// Flag indicating that the callback for the subcommand should be executed immediately on parse completion which is
+    /// before help or ini files are processed.
+    bool immediate_callback_{false};
+
+    /// This is a function that runs prior to the start of parsing
+    std::function<void(size_t)> pre_parse_callback_;
+
     /// This is a function that runs when complete. Great for subcommands. Can throw.
     std::function<void()> callback_;
 
@@ -173,6 +183,11 @@ class App {
     /// specify that positional arguments come at the end of the argument sequence not inheritable
     bool positionals_at_end_{false};
 
+    /// If set to true the subcommand will start each parse disabled
+    bool disabled_by_default_{false};
+    /// If set to true the subcommand will be reenabled at the start of each parse
+    bool enabled_by_default_{false};
+
     /// A pointer to the parent if this is a subcommand
     App *parent_{nullptr};
 
@@ -267,6 +282,13 @@ class App {
         return this;
     }
 
+    /// Set a callback to execute prior to parsing.
+    ///
+    App *preparse_callback(std::function<void(size_t)> pp_callback) {
+        pre_parse_callback_ = std::move(pp_callback);
+        return this;
+    }
+
     /// Set a name for the app (empty will use parser to set the name)
     App *name(std::string app_name = "") {
         name_ = app_name;
@@ -292,6 +314,25 @@ class App {
         return this;
     }
 
+    /// Set the subcommand to be disabled by default, so on clear(), at the start of each parse it is disabled
+    App *disabled_by_default(bool disable = true) {
+        disabled_by_default_ = disable;
+        return this;
+    }
+
+    /// Set the subcommand to be enabled by default, so on clear(), at the start of each parse it is enabled(not
+    /// disabled)
+    App *enabled_by_default(bool enable = true) {
+        enabled_by_default_ = enable;
+        return this;
+    }
+
+    /// Set the subcommand callback to be executed immediately on subcommand completion
+    App *immediate_callback(bool immediate = true) {
+        immediate_callback_ = immediate;
+        return this;
+    }
+
     /// Remove the error when extras are left over on the command line.
     /// Will also call App::allow_extras().
     App *allow_config_extras(bool allow = true) {
@@ -403,7 +444,7 @@ class App {
     }
 
     /// Add option for non-vectors (duplicate copy needed without defaulted to avoid `iostream << value`)
-    template <typename T, enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>
+    template <typename T, enable_if_t<!is_vector<T>::value & !std::is_const<T>::value, detail::enabler> = detail::dummy>
     Option *add_option(std::string option_name,
                        T &variable, ///< The variable to set
                        std::string option_description = "") {
@@ -711,7 +752,7 @@ class App {
 #ifdef CLI11_CPP14
     /// Add option for callback (C++14 or better only)
     Option *add_flag(std::string flag_name,
-                     std::function<void(int64_t)> function, ///< A function to call, void(int)
+                     std::function<void(int64_t)> function, ///< A function to call, void(int64_t)
                      std::string flag_description = "") {
         return add_flag_function(std::move(flag_name), std::move(function), std::move(flag_description));
     }
@@ -1195,13 +1236,15 @@ class App {
     void clear() {
 
         parsed_ = 0;
+        pre_parse_called_ = false;
+
         missing_.clear();
         parsed_subcommands_.clear();
         for(const Option_p &opt : options_) {
             opt->clear();
         }
-        for(const App_p &app : subcommands_) {
-            app->clear();
+        for(const App_p &subc : subcommands_) {
+            subc->clear();
         }
     }
 
@@ -1575,6 +1618,15 @@ class App {
     /// Get the status of disabled
     bool get_disabled() const { return disabled_; }
 
+    /// Get the status of disabled
+    bool get_immediate_callback() const { return immediate_callback_; }
+
+    /// Get the status of disabled by default
+    bool get_disabled_by_default() const { return disabled_by_default_; }
+
+    /// Get the status of disabled by default
+    bool get_enabled_by_default() const { return enabled_by_default_; }
+
     /// Get the status of allow extras
     bool get_allow_config_extras() const { return allow_config_extras_; }
 
@@ -1704,9 +1756,15 @@ class App {
     }
 
     /// configure subcommands to enable parsing through the current object
-    /// set the correct fallthrough and prefix for nameless subcommands and
+    /// set the correct fallthrough and prefix for nameless subcommands and manage the automatic enable or disable
     /// makes sure parent is set correctly
     void _configure() {
+        if(disabled_by_default_) {
+            disabled_ = true;
+        }
+        if(enabled_by_default_) {
+            disabled_ = false;
+        }
         for(const App_p &app : subcommands_) {
             if(app->has_automatic_name_) {
                 app->name_.clear();
@@ -1723,34 +1781,35 @@ class App {
     /// Internal function to run (App) callback, top down
     void run_callback() {
         pre_callback();
-        if(callback_)
+        if(callback_ && (parsed_ > 0))
             callback_();
         for(App *subc : get_subcommands()) {
-            subc->run_callback();
+            if((subc->get_name().empty()) || (!subc->immediate_callback_))
+                subc->run_callback();
         }
     }
 
     /// Check to see if a subcommand is valid. Give up immediately if subcommand max has been reached.
-    bool _valid_subcommand(const std::string &current) const {
+    bool _valid_subcommand(const std::string &current, bool ignore_used = true) const {
         // Don't match if max has been reached - but still check parents
         if(require_subcommand_max_ != 0 && parsed_subcommands_.size() >= require_subcommand_max_) {
-            return parent_ != nullptr && parent_->_valid_subcommand(current);
+            return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
         }
-        auto com = _find_subcommand(current, true, true);
+        auto com = _find_subcommand(current, true, ignore_used);
         if(com != nullptr) {
             return true;
         }
         // Check parent if exists, else return false
-        return parent_ != nullptr && parent_->_valid_subcommand(current);
+        return parent_ != nullptr && parent_->_valid_subcommand(current, ignore_used);
     }
 
     /// Selects a Classifier enum based on the type of the current argument
-    detail::Classifier _recognize(const std::string &current) const {
+    detail::Classifier _recognize(const std::string &current, bool ignore_used_subcommands = true) const {
         std::string dummy1, dummy2;
 
         if(current == "--")
             return detail::Classifier::POSITIONAL_MARK;
-        if(_valid_subcommand(current))
+        if(_valid_subcommand(current, ignore_used_subcommands))
             return detail::Classifier::SUBCOMMAND;
         if(detail::split_long(current, dummy1, dummy2))
             return detail::Classifier::LONG;
@@ -1758,6 +1817,8 @@ class App {
             return detail::Classifier::SHORT;
         if((allow_windows_style_options_) && (detail::split_windows_style(current, dummy1, dummy2)))
             return detail::Classifier::WINDOWS;
+        if((current == "++") && (!name_.empty()))
+            return detail::Classifier::SUBCOMMAND_TERMINATOR;
         return detail::Classifier::NONE;
     }
 
@@ -1811,7 +1872,8 @@ class App {
         }
 
         for(App_p &sub : subcommands_) {
-            sub->_process_env();
+            if((sub->get_name().empty()) || (!sub->immediate_callback_))
+                sub->_process_env();
         }
     }
 
@@ -1824,7 +1886,8 @@ class App {
         }
 
         for(App_p &sub : subcommands_) {
-            sub->_process_callbacks();
+            if((sub->get_name().empty()) || (!sub->immediate_callback_))
+                sub->_process_callbacks();
         }
     }
 
@@ -1952,7 +2015,10 @@ class App {
                     }
                 }
             }
-            sub->_process_requirements();
+            if((sub->count() > 0) || (sub->name_.empty())) {
+                sub->_process_requirements();
+            }
+
             if((sub->required_) && (sub->count_all() == 0)) {
                 throw(CLI::RequiredError(sub->get_display_name()));
             }
@@ -1996,10 +2062,13 @@ class App {
     /// Internal parse function
     void _parse(std::vector<std::string> &args) {
         increment_parsed();
+        _trigger_pre_parse(args.size());
         bool positional_only = false;
 
         while(!args.empty()) {
-            _parse_single(args, positional_only);
+            if(!_parse_single(args, positional_only)) {
+                break;
+            }
         }
 
         if(parent_ == nullptr) {
@@ -2010,6 +2079,12 @@ class App {
 
             // Convert missing (pairs) to extras (string only)
             args = remaining(false);
+        } else if(immediate_callback_) {
+            _process_env();
+            _process_callbacks();
+            _process_help_flags();
+            _process_requirements();
+            run_callback();
         }
     }
 
@@ -2065,18 +2140,26 @@ class App {
     }
 
     /// Parse "one" argument (some may eat more than one), delegate to parent if fails, add to missing if missing
-    /// from master
-    void _parse_single(std::vector<std::string> &args, bool &positional_only) {
-
+    /// from master return false if the parse has failed and needs to return to parent
+    bool _parse_single(std::vector<std::string> &args, bool &positional_only) {
+        bool retval = true;
         detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back());
         switch(classifier) {
         case detail::Classifier::POSITIONAL_MARK:
-            missing_.emplace_back(classifier, args.back());
             args.pop_back();
             positional_only = true;
+            if((!_has_remaining_positionals()) && (parent_ != nullptr)) {
+                retval = false;
+            } else {
+                missing_.emplace_back(classifier, "--");
+            }
+            break;
+        case detail::Classifier::SUBCOMMAND_TERMINATOR:
+            args.pop_back();
+            retval = false;
             break;
         case detail::Classifier::SUBCOMMAND:
-            _parse_subcommand(args);
+            retval = _parse_subcommand(args);
             break;
         case detail::Classifier::LONG:
         case detail::Classifier::SHORT:
@@ -2086,11 +2169,12 @@ class App {
             break;
         case detail::Classifier::NONE:
             // Probably a positional or something for a parent (sub)command
-            _parse_positional(args);
-            if(positionals_at_end_) {
+            retval = _parse_positional(args);
+            if(retval && positionals_at_end_) {
                 positional_only = true;
             }
         }
+        return retval;
     }
 
     /// Count the required remaining positional arguments
@@ -2104,10 +2188,21 @@ class App {
         return retval;
     }
 
+    /// Count the required remaining positional arguments
+    bool _has_remaining_positionals() const {
+        for(const Option_p &opt : options_)
+            if(opt->get_positional() &&
+               ((opt->get_items_expected() < 0) || ((static_cast<int>(opt->count()) < opt->get_items_expected()))))
+                return true;
+
+        return false;
+    }
+
     /// Parse a positional, go up the tree to check
-    void _parse_positional(std::vector<std::string> &args) {
+    /// Return true if the positional was used false otherwise
+    bool _parse_positional(std::vector<std::string> &args) {
 
-        std::string positional = args.back();
+        const std::string &positional = args.back();
         for(const Option_p &opt : options_) {
             // Eat options, one by one, until done
             if(opt->get_positional() &&
@@ -2116,52 +2211,59 @@ class App {
                 opt->add_result(positional);
                 parse_order_.push_back(opt.get());
                 args.pop_back();
-                return;
+                return true;
             }
         }
 
         for(auto &subc : subcommands_) {
             if((subc->name_.empty()) && (!subc->disabled_)) {
-                subc->_parse_positional(args);
-                if(subc->missing_.empty()) { // check if it was used and is not in the missing category
-                    return;
-                } else {
-                    args.push_back(std::move(subc->missing_.front().second));
-                    subc->missing_.clear();
+                if(subc->_parse_positional(args)) {
+                    if(!subc->pre_parse_called_) {
+                        subc->_trigger_pre_parse(args.size());
+                    }
+                    return true;
                 }
             }
         }
-
+        /// let the parent deal with it if possible
         if(parent_ != nullptr && fallthrough_)
-            return parent_->_parse_positional(args);
-        else {
-            /// now try one last gasp at subcommands that have been executed before, go to root app and try to find a
-            /// subcommand in a broader way
-            auto parent_app = this;
-            while(parent_app->parent_ != nullptr) {
-                parent_app = parent_app->parent_;
-            }
-            auto com = parent_app->_find_subcommand(args.back(), true, false);
-            if((com != nullptr) &&
-               ((com->parent_->require_subcommand_max_ == 0) ||
-                (com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size()))) {
-                args.pop_back();
-                com->_parse(args);
-            } else {
-                if(positionals_at_end_) {
-                    throw CLI::ExtrasError(args);
-                }
-                args.pop_back();
-                missing_.emplace_back(detail::Classifier::NONE, positional);
+            return _get_fallthrough_parent()->_parse_positional(args);
 
-                if(prefix_command_) {
-                    while(!args.empty()) {
-                        missing_.emplace_back(detail::Classifier::NONE, args.back());
-                        args.pop_back();
-                    }
-                }
+        /// Try to find a local subcommand that is repeated
+        auto com = _find_subcommand(args.back(), true, false);
+        if((com != nullptr) &&
+           ((require_subcommand_max_ == 0) || (require_subcommand_max_ > parsed_subcommands_.size()))) {
+            args.pop_back();
+            com->_parse(args);
+            return true;
+        }
+        /// now try one last gasp at subcommands that have been executed before, go to root app and try to find a
+        /// subcommand in a broader way, if one exists let the parent deal with it
+        auto parent_app = (parent_ != nullptr) ? _get_fallthrough_parent() : this;
+        com = parent_app->_find_subcommand(args.back(), true, false);
+        if((com != nullptr) && ((com->parent_->require_subcommand_max_ == 0) ||
+                                (com->parent_->require_subcommand_max_ > com->parent_->parsed_subcommands_.size()))) {
+            return false;
+        }
+
+        if(positionals_at_end_) {
+            throw CLI::ExtrasError(args);
+        }
+        /// If this is an option group don't deal with it
+        if((parent_ != nullptr) && (name_.empty())) {
+            return false;
+        }
+        /// We are out of other options this goes to missing
+        missing_.emplace_back(detail::Classifier::NONE, positional);
+        args.pop_back();
+        if(prefix_command_) {
+            while(!args.empty()) {
+                missing_.emplace_back(detail::Classifier::NONE, args.back());
+                args.pop_back();
             }
         }
+
+        return true;
     }
 
     /// Locate a subcommand by name with two conditions, should disabled subcommands be ignored, and should used
@@ -2186,27 +2288,34 @@ class App {
     /// Parse a subcommand, modify args and continue
     ///
     /// Unlike the others, this one will always allow fallthrough
-    void _parse_subcommand(std::vector<std::string> &args) {
-        if(_count_remaining_positionals(/* required */ true) > 0)
-            return _parse_positional(args);
+    /// return true if the subcommand was processed false otherwise
+    bool _parse_subcommand(std::vector<std::string> &args) {
+        if(_count_remaining_positionals(/* required */ true) > 0) {
+            _parse_positional(args);
+            return true;
+        }
         auto com = _find_subcommand(args.back(), true, true);
         if(com != nullptr) {
             args.pop_back();
-            if(std::find(std::begin(parsed_subcommands_), std::end(parsed_subcommands_), com) ==
-               std::end(parsed_subcommands_))
-                parsed_subcommands_.push_back(com);
+            parsed_subcommands_.push_back(com);
             com->_parse(args);
-            return;
+            auto parent_app = com->parent_;
+            while(parent_app != this) {
+                parent_app->_trigger_pre_parse(args.size());
+                parent_app->parsed_subcommands_.push_back(com);
+                parent_app = parent_app->parent_;
+            }
+            return true;
         }
 
         if(parent_ == nullptr)
             throw HorribleError("Subcommand " + args.back() + " missing");
-
-        return parent_->_parse_subcommand(args);
+        return false;
     }
 
     /// Parse a short (false) or long (true) argument, must be at the top of the list
-    void _parse_arg(std::vector<std::string> &args, detail::Classifier current_type) {
+    /// return true if the argument was processed or false if nothing was done
+    bool _parse_arg(std::vector<std::string> &args, detail::Classifier current_type) {
 
         std::string current = args.back();
 
@@ -2245,25 +2354,25 @@ class App {
         if(op_ptr == std::end(options_)) {
             for(auto &subc : subcommands_) {
                 if((subc->name_.empty()) && (!(subc->disabled_))) {
-                    subc->_parse_arg(args, current_type);
-                    if(subc->missing_.empty()) { // check if it was used and is not in the missing category
-                        return;
-                    } else {
-                        // for unnamed subs they shouldn't trigger a missing argument
-                        args.push_back(std::move(subc->missing_.front().second));
-                        subc->missing_.clear();
+                    if(subc->_parse_arg(args, current_type)) {
+                        if(!subc->pre_parse_called_) {
+                            subc->_trigger_pre_parse(args.size());
+                        }
+                        return true;
                     }
                 }
             }
             // If a subcommand, try the master command
             if(parent_ != nullptr && fallthrough_)
-                return parent_->_parse_arg(args, current_type);
-            // Otherwise, add to missing
-            else {
-                args.pop_back();
-                missing_.emplace_back(current_type, current);
-                return;
+                return _get_fallthrough_parent()->_parse_arg(args, current_type);
+            // don't capture missing if this is a nameless subcommand
+            if((parent_ != nullptr) && (name_.empty())) {
+                return false;
             }
+            // Otherwise, add to missing
+            args.pop_back();
+            missing_.emplace_back(current_type, current);
+            return true;
         }
 
         args.pop_back();
@@ -2304,7 +2413,7 @@ class App {
 
         // Unlimited vector parser
         if(num < 0) {
-            while(!args.empty() && _recognize(args.back()) == detail::Classifier::NONE) {
+            while(!args.empty() && _recognize(args.back(), false) == detail::Classifier::NONE) {
                 if(collected >= -num) {
                     // We could break here for allow extras, but we don't
 
@@ -2340,6 +2449,38 @@ class App {
             rest = "-" + rest;
             args.push_back(rest);
         }
+        return true;
+    }
+
+    /// Trigger the pre_parse callback if needed
+    void _trigger_pre_parse(size_t remaining_args) {
+        if(!pre_parse_called_) {
+            pre_parse_called_ = true;
+            if(pre_parse_callback_) {
+                pre_parse_callback_(remaining_args);
+            }
+        } else if(immediate_callback_) {
+            if(!name_.empty()) {
+                auto pcnt = parsed_;
+                auto extras = std::move(missing_);
+                clear();
+                parsed_ = pcnt;
+                pre_parse_called_ = true;
+                missing_ = std::move(extras);
+            }
+        }
+    }
+
+    /// Get the appropriate parent to fallthrough to which is the first one that has a name or the main app
+    App *_get_fallthrough_parent() {
+        if(parent_ == nullptr) {
+            throw(HorribleError("No Valid parent"));
+        }
+        auto fallthrough_parent = parent_;
+        while((fallthrough_parent->parent_ != nullptr) && (fallthrough_parent->get_name().empty())) {
+            fallthrough_parent = fallthrough_parent->parent_;
+        }
+        return fallthrough_parent;
     }
 
   public:
@@ -2390,6 +2531,7 @@ class Option_group : public App {
     Option_group(std::string group_description, std::string group_name, App *parent)
         : App(std::move(group_description), "", parent) {
         group(group_name);
+        // option groups should have automatic fallthrough
     }
     using App::add_option;
     /// add an existing option to the Option_group
@@ -2408,6 +2550,47 @@ class Option_group : public App {
         add_options(args...);
     }
 };
+/// Helper function to enable one option group/subcommand when another is used
+inline void TriggerOn(App *trigger_app, App *app_to_enable) {
+    app_to_enable->enabled_by_default(false);
+    app_to_enable->disabled_by_default();
+    trigger_app->preparse_callback([app_to_enable](size_t) { app_to_enable->disabled(false); });
+}
+
+/// Helper function to enable one option group/subcommand when another is used
+inline void TriggerOn(App *trigger_app, std::vector<App *> apps_to_enable) {
+    for(auto &app : apps_to_enable) {
+        app->enabled_by_default(false);
+        app->disabled_by_default();
+    }
+
+    trigger_app->preparse_callback([apps_to_enable](size_t) {
+        for(auto &app : apps_to_enable) {
+            app->disabled(false);
+        }
+    });
+}
+
+/// Helper function to disable one option group/subcommand when another is used
+inline void TriggerOff(App *trigger_app, App *app_to_enable) {
+    app_to_enable->disabled_by_default(false);
+    app_to_enable->enabled_by_default();
+    trigger_app->preparse_callback([app_to_enable](size_t) { app_to_enable->disabled(); });
+}
+
+/// Helper function to disable one option group/subcommand when another is used
+inline void TriggerOff(App *trigger_app, std::vector<App *> apps_to_enable) {
+    for(auto &app : apps_to_enable) {
+        app->disabled_by_default(false);
+        app->enabled_by_default();
+    }
+
+    trigger_app->preparse_callback([apps_to_enable](size_t) {
+        for(auto &app : apps_to_enable) {
+            app->disabled();
+        }
+    });
+}
 
 namespace FailureMessage {
 
@@ -2456,6 +2639,8 @@ struct AppFriend {
         typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type {
         return app->_parse_subcommand(std::forward<Args>(args)...);
     }
+    /// Wrap the fallthrough parent function to make sure that is working correctly
+    static App *get_fallthrough_parent(App *app) { return app->_get_fallthrough_parent(); }
 };
 } // namespace detail
 
diff --git a/include/CLI/Option.hpp b/include/CLI/Option.hpp
index 5bb258d9a..44a3bc345 100644
--- a/include/CLI/Option.hpp
+++ b/include/CLI/Option.hpp
@@ -76,7 +76,6 @@ template <typename CRTP> class OptionBase {
     CRTP *group(std::string name) {
         group_ = name;
         return static_cast<CRTP *>(this);
-        ;
     }
 
     /// Set the option as required
diff --git a/tests/AppTest.cpp b/tests/AppTest.cpp
index 5a9d8731d..9349e910e 100644
--- a/tests/AppTest.cpp
+++ b/tests/AppTest.cpp
@@ -1169,9 +1169,9 @@ TEST_F(TApp, RequiredFlags) {
 
 TEST_F(TApp, CallbackFlags) {
 
-    size_t value = 0;
+    int64_t value = 0;
 
-    auto func = [&value](size_t x) { value = x; };
+    auto func = [&value](int64_t x) { value = x; };
 
     app.add_flag_function("-v", func);
 
@@ -1271,9 +1271,9 @@ TEST_F(TApp, CallbackFlagsFalseShortcut) {
 #if __cplusplus >= 201402L || _MSC_VER >= 1900
 TEST_F(TApp, CallbackFlagsAuto) {
 
-    size_t value = 0;
+    int64_t value = 0;
 
-    auto func = [&value](size_t x) { value = x; };
+    auto func = [&value](int64_t x) { value = x; };
 
     app.add_flag("-v", func);
 
@@ -1843,6 +1843,31 @@ TEST_F(TApp, CheckSubcomFail) {
     EXPECT_THROW(CLI::detail::AppFriend::parse_subcommand(&app, args), CLI::HorribleError);
 }
 
+TEST_F(TApp, FallthroughParentFail) {
+    EXPECT_THROW(CLI::detail::AppFriend::get_fallthrough_parent(&app), CLI::HorribleError);
+}
+
+TEST_F(TApp, FallthroughParents) {
+    auto sub = app.add_subcommand("test");
+    EXPECT_EQ(CLI::detail::AppFriend::get_fallthrough_parent(sub), &app);
+
+    auto ssub = sub->add_subcommand("sub2");
+    EXPECT_EQ(CLI::detail::AppFriend::get_fallthrough_parent(ssub), sub);
+
+    auto og1 = app.add_option_group("g1");
+    auto og2 = og1->add_option_group("g2");
+    auto og3 = og2->add_option_group("g3");
+    EXPECT_EQ(CLI::detail::AppFriend::get_fallthrough_parent(og3), &app);
+
+    auto ogb1 = sub->add_option_group("g1");
+    auto ogb2 = ogb1->add_option_group("g2");
+    auto ogb3 = ogb2->add_option_group("g3");
+    EXPECT_EQ(CLI::detail::AppFriend::get_fallthrough_parent(ogb3), sub);
+
+    ogb2->name("groupb");
+    EXPECT_EQ(CLI::detail::AppFriend::get_fallthrough_parent(ogb3), ogb2);
+}
+
 TEST_F(TApp, OptionWithDefaults) {
     int someint = 2;
     app.add_option("-a", someint, "", true);
@@ -1953,8 +1978,8 @@ TEST_F(TApp, EmptyOptionFail) {
 }
 
 TEST_F(TApp, BeforeRequirements) {
-    app.add_flag_function("-a", [](size_t) { throw CLI::Success(); });
-    app.add_flag_function("-b", [](size_t) { throw CLI::CallForHelp(); });
+    app.add_flag_function("-a", [](int64_t) { throw CLI::Success(); });
+    app.add_flag_function("-b", [](int64_t) { throw CLI::CallForHelp(); });
 
     args = {"extra"};
     EXPECT_THROW(run(), CLI::ExtrasError);
diff --git a/tests/OptionGroupTest.cpp b/tests/OptionGroupTest.cpp
index 0321569f0..e8f12f5e5 100644
--- a/tests/OptionGroupTest.cpp
+++ b/tests/OptionGroupTest.cpp
@@ -517,12 +517,12 @@ TEST_F(ManyGroups, SameSubcommand) {
     EXPECT_TRUE(*sub2);
     EXPECT_TRUE(*sub3);
     /// This should be made to work at some point
-    /*auto subs = app.get_subcommands();
+    auto subs = app.get_subcommands();
     EXPECT_EQ(subs.size(), 3u);
     EXPECT_EQ(subs[0], sub1);
     EXPECT_EQ(subs[1], sub2);
     EXPECT_EQ(subs[2], sub3);
-    */
+
     args = {"sub1", "sub1", "sub1", "sub1"};
     // for the 4th and future ones they will route to the first one
     run();
@@ -531,9 +531,107 @@ TEST_F(ManyGroups, SameSubcommand) {
     EXPECT_EQ(sub3->count(), 1u);
 
     // subs should remain the same since the duplicate would not be registered there
-    /*subs = app.get_subcommands();
+    subs = app.get_subcommands();
     EXPECT_EQ(subs.size(), 3u);
     EXPECT_EQ(subs[0], sub1);
     EXPECT_EQ(subs[1], sub2);
-    EXPECT_EQ(subs[2], sub3);*/
+    EXPECT_EQ(subs[2], sub3);
+}
+
+struct ManyGroupsPreTrigger : public ManyGroups {
+    size_t triggerMain, trigger1{87u}, trigger2{34u}, trigger3{27u};
+    ManyGroupsPreTrigger() {
+        remove_required();
+        app.preparse_callback([this](size_t count) { triggerMain = count; });
+
+        g1->preparse_callback([this](size_t count) { trigger1 = count; });
+        g2->preparse_callback([this](size_t count) { trigger2 = count; });
+        g3->preparse_callback([this](size_t count) { trigger3 = count; });
+    }
+};
+
+TEST_F(ManyGroupsPreTrigger, PreTriggerTestsOptions) {
+
+    args = {"--name1", "test", "--name2", "test3"};
+    run();
+    EXPECT_EQ(triggerMain, 4u);
+    EXPECT_EQ(trigger1, 2u);
+    EXPECT_EQ(trigger2, 0u);
+    EXPECT_EQ(trigger3, 27u);
+
+    args = {"--name1", "test"};
+    trigger2 = 34u;
+    run();
+    EXPECT_EQ(triggerMain, 2u);
+    EXPECT_EQ(trigger1, 0u);
+    EXPECT_EQ(trigger2, 34u);
+
+    args = {};
+    run();
+    EXPECT_EQ(triggerMain, 0u);
+
+    args = {"--name1", "test", "--val1", "45", "--name2", "test3", "--name3=test3", "--val2=37"};
+    run();
+    EXPECT_EQ(triggerMain, 8u);
+    EXPECT_EQ(trigger1, 6u);
+    EXPECT_EQ(trigger2, 2u);
+    EXPECT_EQ(trigger3, 1u);
+}
+
+TEST_F(ManyGroupsPreTrigger, PreTriggerTestsPositionals) {
+    // only 1 group can be used
+    g1->add_option("pos1");
+    g2->add_option("pos2");
+    g3->add_option("pos3");
+
+    args = {"pos1"};
+    run();
+    EXPECT_EQ(triggerMain, 1u);
+    EXPECT_EQ(trigger1, 0u);
+    EXPECT_EQ(trigger2, 34u);
+    EXPECT_EQ(trigger3, 27u);
+
+    args = {"pos1", "pos2"};
+    run();
+    EXPECT_EQ(triggerMain, 2u);
+    EXPECT_EQ(trigger1, 1u);
+    EXPECT_EQ(trigger2, 0u);
+
+    args = {"pos1", "pos2", "pos3"};
+    run();
+    EXPECT_EQ(triggerMain, 3u);
+    EXPECT_EQ(trigger1, 2u);
+    EXPECT_EQ(trigger2, 1u);
+    EXPECT_EQ(trigger3, 0u);
+}
+
+TEST_F(ManyGroupsPreTrigger, PreTriggerTestsSubcommand) {
+
+    auto sub1 = g1->add_subcommand("sub1")->fallthrough();
+    g2->add_subcommand("sub2")->fallthrough();
+    g3->add_subcommand("sub3")->fallthrough();
+
+    size_t subtrigger;
+    sub1->preparse_callback([&subtrigger](size_t count) { subtrigger = count; });
+    args = {"sub1"};
+    run();
+    EXPECT_EQ(triggerMain, 1u);
+    EXPECT_EQ(trigger1, 0u);
+    EXPECT_EQ(trigger2, 34u);
+    EXPECT_EQ(trigger3, 27u);
+
+    args = {"sub1", "sub2"};
+    run();
+    EXPECT_EQ(triggerMain, 2u);
+    EXPECT_EQ(subtrigger, 1u);
+    EXPECT_EQ(trigger1, 1u);
+    EXPECT_EQ(trigger2, 0u);
+
+    args = {"sub2", "sub3", "--name1=test", "sub1"};
+    run();
+    EXPECT_EQ(triggerMain, 4u);
+    EXPECT_EQ(trigger1, 1u);
+    EXPECT_EQ(trigger2, 3u);
+    EXPECT_EQ(trigger3, 1u); // processes the first argument in group3 which includes the entire subcommand, which will
+                             // go until the sub1 command is given
 }
diff --git a/tests/SubcommandTest.cpp b/tests/SubcommandTest.cpp
index 89561f4b4..4b35ab5d7 100644
--- a/tests/SubcommandTest.cpp
+++ b/tests/SubcommandTest.cpp
@@ -170,12 +170,50 @@ TEST_F(TApp, DuplicateSubcommands) {
     args = {"foo", "foo"};
     run();
     EXPECT_TRUE(*foo);
-    EXPECT_EQ(foo->count(), 2);
+    EXPECT_EQ(foo->count(), 2u);
 
     args = {"foo", "foo", "foo"};
     run();
     EXPECT_TRUE(*foo);
-    EXPECT_EQ(foo->count(), 3);
+    EXPECT_EQ(foo->count(), 3u);
+}
+
+TEST_F(TApp, DuplicateSubcommandCallbacks) {
+
+    auto foo = app.add_subcommand("foo");
+    int count = 0;
+    foo->callback([&count]() { ++count; });
+    foo->immediate_callback();
+    EXPECT_TRUE(foo->get_immediate_callback());
+    args = {"foo", "foo"};
+    run();
+    EXPECT_EQ(count, 2);
+    count = 0;
+    args = {"foo", "foo", "foo"};
+    run();
+    EXPECT_EQ(count, 3);
+}
+
+TEST_F(TApp, DuplicateSubcommandCallbacksValues) {
+
+    auto foo = app.add_subcommand("foo");
+    int val;
+    foo->add_option("--val", val);
+    std::vector<int> vals;
+    foo->callback([&vals, &val]() { vals.push_back(val); });
+    foo->immediate_callback();
+    args = {"foo", "--val=45", "foo", "--val=27"};
+    run();
+    EXPECT_EQ(vals.size(), 2u);
+    EXPECT_EQ(vals[0], 45);
+    EXPECT_EQ(vals[1], 27);
+    vals.clear();
+    args = {"foo", "--val=45", "foo", "--val=27", "foo", "--val=36"};
+    run();
+    EXPECT_EQ(vals.size(), 3u);
+    EXPECT_EQ(vals[0], 45);
+    EXPECT_EQ(vals[1], 27);
+    EXPECT_EQ(vals[2], 36);
 }
 
 TEST_F(TApp, Callbacks) {
@@ -238,6 +276,33 @@ TEST_F(TApp, NoFallThroughPositionals) {
     EXPECT_THROW(run(), CLI::ExtrasError);
 }
 
+TEST_F(TApp, NoFallThroughOptsWithTerminator) {
+    int val = 1;
+    app.add_option("--val", val);
+
+    app.add_subcommand("sub");
+
+    args = {"sub", "++", "--val", "2"};
+    run();
+    EXPECT_EQ(val, 2);
+}
+
+TEST_F(TApp, NoFallThroughPositionalsWithTerminator) {
+    int val = 1;
+    app.add_option("val", val);
+
+    app.add_subcommand("sub");
+
+    args = {"sub", "++", "2"};
+    run();
+    EXPECT_EQ(val, 2);
+
+    // try with positional only mark
+    args = {"sub", "--", "3"};
+    run();
+    EXPECT_EQ(val, 3);
+}
+
 TEST_F(TApp, NamelessSubComPositionals) {
 
     auto sub = app.add_subcommand();
@@ -435,6 +500,25 @@ TEST_F(TApp, CallbackOrdering) {
     EXPECT_EQ(2, sub_val);
 }
 
+TEST_F(TApp, CallbackOrderingImmediate) {
+    app.fallthrough();
+    int val = 1, sub_val = 0;
+    app.add_option("--val", val);
+
+    auto sub = app.add_subcommand("sub")->immediate_callback();
+    sub->callback([&val, &sub_val]() { sub_val = val; });
+
+    args = {"sub", "--val=2"};
+    run();
+    EXPECT_EQ(2, val);
+    EXPECT_EQ(1, sub_val);
+
+    args = {"--val=2", "sub"};
+    run();
+    EXPECT_EQ(2, val);
+    EXPECT_EQ(2, sub_val);
+}
+
 TEST_F(TApp, RequiredSubCom) {
     app.add_subcommand("sub1");
     app.add_subcommand("sub2");
@@ -780,9 +864,9 @@ TEST_F(SubcommandProgram, OrderedExtras) {
 
     run();
 
-    EXPECT_EQ(app.remaining(), std::vector<std::string>({"one", "two"}));
-    EXPECT_EQ(start->remaining(), std::vector<std::string>({"three", "--", "four"}));
-    EXPECT_EQ(app.remaining(true), std::vector<std::string>({"one", "two", "three", "--", "four"}));
+    EXPECT_EQ(app.remaining(), std::vector<std::string>({"one", "two", "four"}));
+    EXPECT_EQ(start->remaining(), std::vector<std::string>({"three"}));
+    EXPECT_EQ(app.remaining(true), std::vector<std::string>({"one", "two", "four", "three"}));
 }
 
 TEST_F(SubcommandProgram, MixedOrderExtras) {
@@ -1044,6 +1128,43 @@ TEST_F(ManySubcommands, SubcommandDisabled) {
     args = {"sub3", "sub4"};
     EXPECT_NO_THROW(run());
 }
+
+TEST_F(ManySubcommands, SubcommandTriggeredOff) {
+
+    app.allow_extras(false);
+    sub1->allow_extras(false);
+    sub2->allow_extras(false);
+    CLI::TriggerOff(sub1, sub2);
+    args = {"sub1", "sub2"};
+    EXPECT_THROW(run(), CLI::ExtrasError);
+
+    args = {"sub2", "sub1", "sub3"};
+    EXPECT_NO_THROW(run());
+    CLI::TriggerOff(sub1, {sub3, sub4});
+    EXPECT_THROW(run(), CLI::ExtrasError);
+    args = {"sub1", "sub2", "sub4"};
+    EXPECT_THROW(run(), CLI::ExtrasError);
+}
+
+TEST_F(ManySubcommands, SubcommandTriggeredOn) {
+
+    app.allow_extras(false);
+    sub1->allow_extras(false);
+    sub2->allow_extras(false);
+    CLI::TriggerOn(sub1, sub2);
+    args = {"sub1", "sub2"};
+    EXPECT_NO_THROW(run());
+
+    args = {"sub2", "sub1", "sub4"};
+    EXPECT_THROW(run(), CLI::ExtrasError);
+    CLI::TriggerOn(sub1, {sub3, sub4});
+    sub2->disabled_by_default(false);
+    sub2->disabled(false);
+    EXPECT_NO_THROW(run());
+    args = {"sub3", "sub1", "sub2"};
+    EXPECT_THROW(run(), CLI::ExtrasError);
+}
+
 TEST_F(TApp, UnnamedSub) {
     double val;
     auto sub = app.add_subcommand("", "empty name");
@@ -1205,3 +1326,40 @@ TEST_F(ManySubcommands, getSubtests) {
     CLI::App_p sub3p = app.get_subcommand_ptr(2);
     EXPECT_EQ(sub3p.get(), sub3);
 }
+
+TEST_F(ManySubcommands, defaultDisabledSubcommand) {
+
+    sub1->fallthrough();
+    sub2->disabled_by_default();
+    run();
+    auto rem = app.remaining();
+    EXPECT_EQ(rem.size(), 1u);
+    EXPECT_EQ(rem[0], "sub2");
+    EXPECT_TRUE(sub2->get_disabled_by_default());
+    sub2->disabled(false);
+    EXPECT_FALSE(sub2->get_disabled());
+    run();
+    // this should disable it again even though it was disabled
+    rem = app.remaining();
+    EXPECT_EQ(rem.size(), 1u);
+    EXPECT_EQ(rem[0], "sub2");
+    EXPECT_TRUE(sub2->get_disabled_by_default());
+    EXPECT_TRUE(sub2->get_disabled());
+}
+
+TEST_F(ManySubcommands, defaultEnabledSubcommand) {
+
+    sub2->enabled_by_default();
+    run();
+    auto rem = app.remaining();
+    EXPECT_EQ(rem.size(), 0u);
+    EXPECT_TRUE(sub2->get_enabled_by_default());
+    sub2->disabled();
+    EXPECT_TRUE(sub2->get_disabled());
+    run();
+    // this should disable it again even though it was disabled
+    rem = app.remaining();
+    EXPECT_EQ(rem.size(), 0u);
+    EXPECT_TRUE(sub2->get_enabled_by_default());
+    EXPECT_FALSE(sub2->get_disabled());
+}