diff --git a/force_bdss/base_core_driver.py b/force_bdss/base_core_driver.py
index 7f0fa6db586df4ecf71a8a388cc746f910d87a02..b3c2702ef10a8e7750dd01fa9ced87a22e2c70c8 100644
--- a/force_bdss/base_core_driver.py
+++ b/force_bdss/base_core_driver.py
@@ -37,27 +37,27 @@ class BaseCoreDriver(Plugin):
         List(IKPICalculatorBundle),
         id='force.bdss.kpi_calculators.bundles')
 
-    def _data_source_bundle_by_name(self, name):
+    def _data_source_bundle_by_id(self, id):
         for ds in self.data_source_bundles:
-            if ds.name == name:
+            if ds.id == id:
                 return ds
 
         raise Exception("Requested data source {} but don't know "
-                        "to find it.".format(name))
+                        "to find it.".format(id))
 
-    def _kpi_calculator_bundle_by_name(self, name):
+    def _kpi_calculator_bundle_by_id(self, id):
         for kpic in self.kpi_calculator_bundles:
-            if kpic.name == name:
+            if kpic.id == id:
                 return kpic
 
         raise Exception(
             "Requested kpi calculator {} but don't know "
-            "to find it.".format(name))
+            "to find it.".format(id))
 
-    def _mco_bundle_by_name(self, name):
+    def _mco_bundle_by_id(self, id):
         for mco in self.mco_bundles:
-            if mco.name == name:
+            if mco.id == id:
                 return mco
 
         raise Exception("Requested MCO {} but it's not available"
-                        "to compute it.".format(name))
+                        "to compute it.".format(id))
diff --git a/force_bdss/bdss_application.py b/force_bdss/bdss_application.py
index 64537cfa1a72ed6ab9a6db1aca9a99ca48a54342..a0ac1141945a0ef434a55592bed537bd5011a163 100644
--- a/force_bdss/bdss_application.py
+++ b/force_bdss/bdss_application.py
@@ -54,7 +54,7 @@ class BDSSApplication(Application):
         except NoMatches:
             print("No extensions found")
 
-        super().__init__(plugins=plugins)
+        super(BDSSApplication, self).__init__(plugins=plugins)
 
     def _workflow_default(self):
         with open(self.workflow_filepath) as f:
diff --git a/force_bdss/cli/force_bdss.py b/force_bdss/cli/force_bdss.py
index 32807502939a5bf92e55d58c4ce6d4dd4873b081..48ce95353a49a5531c68bd09d4e441c7151179d7 100644
--- a/force_bdss/cli/force_bdss.py
+++ b/force_bdss/cli/force_bdss.py
@@ -2,6 +2,11 @@ import click
 
 from ..bdss_application import BDSSApplication
 
+# Makes the application rethrow the exception so that it exits return code
+# different from zero.
+from traits.api import push_exception_handler
+push_exception_handler(reraise_exceptions=True)
+
 
 @click.command()
 @click.option("--evaluate", is_flag=True)
diff --git a/force_bdss/cli/tests/__init__.py b/force_bdss/cli/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/examples/foo.csv b/force_bdss/cli/tests/fixtures/foo.csv
similarity index 100%
rename from examples/foo.csv
rename to force_bdss/cli/tests/fixtures/foo.csv
diff --git a/examples/test_csv.json b/force_bdss/cli/tests/fixtures/test_csv.json
similarity index 71%
rename from examples/test_csv.json
rename to force_bdss/cli/tests/fixtures/test_csv.json
index 0fe50484a4c12813bcb882f6ea2d865aa9aac6bb..80437d0abf8ad92fd7d5529f3ab36fd5c614094e 100644
--- a/examples/test_csv.json
+++ b/force_bdss/cli/tests/fixtures/test_csv.json
@@ -1,13 +1,13 @@
 {
   "multi_criteria_optimizer": {
-    "name": "dakota",
+    "id": "force.bdss.bundles.enthought.dakota",
     "model_data": {
       "value_types": ["DUMMY"]
     }
   },
   "data_sources": [
     {
-      "name": "csv_extractor",
+      "id": "force.bdss.bundles.enthought.csv_extractor",
       "model_data": {
         "filename": "foo.csv",
         "row": 3,
@@ -16,7 +16,7 @@
       }
     },
     {
-      "name": "csv_extractor",
+      "id": "force.bdss.bundles.enthought.csv_extractor",
       "model_data": {
         "filename": "foo.csv",
         "row": 3,
@@ -27,7 +27,7 @@
   ],
   "kpi_calculators": [
     {
-      "name": "kpi_adder",
+      "id": "force.bdss.bundles.enthought.kpi_adder",
       "model_data": {
         "cuba_type_in": "PRESSURE",
         "cuba_type_out": "TOTAL_PRESSURE"
diff --git a/force_bdss/cli/tests/test_execution.py b/force_bdss/cli/tests/test_execution.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e4d28d928a9847fa8daa58aec65a4576ab2cc2c
--- /dev/null
+++ b/force_bdss/cli/tests/test_execution.py
@@ -0,0 +1,31 @@
+import unittest
+import subprocess
+import os
+from contextlib import contextmanager
+
+
+@contextmanager
+def cd(dir):
+    cwd = os.curdir
+    os.chdir(dir)
+    try:
+        yield
+    finally:
+        os.chdir(cwd)
+
+
+def fixture_dir():
+    return os.path.join(
+        os.path.dirname(os.path.abspath(__file__)),
+        "fixtures")
+
+
+class TestExecution(unittest.TestCase):
+    def test_plain_invocation_mco(self):
+        with cd(fixture_dir()):
+            out = subprocess.check_call(["force_bdss", "test_csv.json"])
+            self.assertEqual(out, 0)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/force_bdss/core_evaluation_driver.py b/force_bdss/core_evaluation_driver.py
index 831c539cf39ba31b68a97ea21c6f3eb43ad8cc32..bf6c848c7b0acf3c400601eaaf19e1c167788bdd 100644
--- a/force_bdss/core_evaluation_driver.py
+++ b/force_bdss/core_evaluation_driver.py
@@ -13,7 +13,7 @@ class CoreEvaluationDriver(BaseCoreDriver):
         workflow = self.application.workflow
 
         mco_data = workflow.multi_criteria_optimizer
-        mco_bundle = self._mco_bundle_by_name(mco_data.name)
+        mco_bundle = self._mco_bundle_by_id(mco_data.id)
         mco_model = mco_bundle.create_model(mco_data.model_data)
         mco_communicator = mco_bundle.create_communicator(
             self.application,
@@ -23,8 +23,8 @@ class CoreEvaluationDriver(BaseCoreDriver):
 
         ds_results = []
         for requested_ds in workflow.data_sources:
-            ds_bundle = self._data_source_bundle_by_name(
-                requested_ds.name)
+            ds_bundle = self._data_source_bundle_by_id(
+                requested_ds.id)
             ds_model = ds_bundle.create_model(requested_ds.model_data)
             data_source = ds_bundle.create_data_source(
                 self.application, ds_model)
@@ -32,8 +32,8 @@ class CoreEvaluationDriver(BaseCoreDriver):
 
         kpi_results = []
         for requested_kpic in workflow.kpi_calculators:
-            kpic_bundle = self._kpi_calculator_bundle_by_name(
-                requested_kpic.name)
+            kpic_bundle = self._kpi_calculator_bundle_by_id(
+                requested_kpic.id)
             ds_model = kpic_bundle.create_model(
                 requested_kpic.model_data)
             kpi_calculator = kpic_bundle.create_data_source(
diff --git a/force_bdss/core_mco_driver.py b/force_bdss/core_mco_driver.py
index cc296dae4a20930f41d0d5a54755965db848661e..f1cb9b483ac0727f6afcb813d4f0c0c933db836e 100644
--- a/force_bdss/core_mco_driver.py
+++ b/force_bdss/core_mco_driver.py
@@ -13,7 +13,7 @@ class CoreMCODriver(BaseCoreDriver):
         workflow = self.application.workflow
 
         mco_data = workflow.multi_criteria_optimizer
-        mco_bundle = self._mco_bundle_by_name(mco_data.name)
+        mco_bundle = self._mco_bundle_by_id(mco_data.id)
         mco_model = mco_bundle.create_model(mco_data.model_data)
         mco = mco_bundle.create_optimizer(self.application, mco_model)
 
diff --git a/force_bdss/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py b/force_bdss/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py
index b0a2e474c9bd49ca1aa6b224c555dfd9e742c703..295c44dc306b3816569845dcff7c2228f0c2b28e 100644
--- a/force_bdss/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py
+++ b/force_bdss/core_plugins/csv_extractor/csv_extractor/csv_extractor_bundle.py
@@ -2,6 +2,7 @@ from traits.api import provides, HasStrictTraits
 from traits.trait_types import String
 
 from force_bdss.data_sources.i_data_source_bundle import IDataSourceBundle
+from force_bdss.id_generators import bundle_id
 
 from .csv_extractor_model import CSVExtractorModel
 from .csv_extractor_data_source import CSVExtractorDataSource
@@ -9,7 +10,7 @@ from .csv_extractor_data_source import CSVExtractorDataSource
 
 @provides(IDataSourceBundle)
 class CSVExtractorBundle(HasStrictTraits):
-    name = String("csv_extractor")
+    id = String(bundle_id("enthought", "csv_extractor"))
 
     def create_model(self, model_data=None):
         if model_data is None:
diff --git a/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py b/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py
index 8f10341086f0a315270f63734855aa8ad4e18289..2dcba7e3bfd078a88ba46a0f2d70b0768ef5801a 100644
--- a/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py
+++ b/force_bdss/core_plugins/csv_extractor/csv_extractor_plugin.py
@@ -7,8 +7,6 @@ from .csv_extractor.csv_extractor_bundle import CSVExtractorBundle
 
 
 class CSVExtractorPlugin(Plugin):
-    id = "force.bdss.data_sources.csv_extractor"
-
     data_sources = List(
         IDataSourceBundle,
         contributes_to='force.bdss.data_sources.bundles'
diff --git a/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py b/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py
index 336cb374008c189534801ee93f9b74ede066abd4..e23e441e51d15344ab50d6a7ebdf2498aca2475e 100644
--- a/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py
+++ b/force_bdss/core_plugins/test_kpi/kpi_adder/kpi_adder_bundle.py
@@ -1,6 +1,7 @@
 from traits.api import provides, HasStrictTraits
 from traits.trait_types import String
 
+from force_bdss.id_generators import bundle_id
 from force_bdss.kpi.i_kpi_calculator_bundle import IKPICalculatorBundle
 
 from .kpi_adder_model import KPIAdderModel
@@ -9,7 +10,7 @@ from .kpi_adder_calculator import KPIAdderCalculator
 
 @provides(IKPICalculatorBundle)
 class KPIAdderBundle(HasStrictTraits):
-    name = String("kpi_adder")
+    id = String(bundle_id("enthought", "kpi_adder"))
 
     def create_model(self, model_data=None):
         if model_data is None:
diff --git a/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py b/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py
index 3be48fb51abc72a16d7a2fb8d40ff16b27e2207c..5250693b5e1ca20905d1185089fdaa8dbe524537 100644
--- a/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py
+++ b/force_bdss/core_plugins/test_kpi/test_kpi_calculator_plugin.py
@@ -8,8 +8,6 @@ from .kpi_adder.kpi_adder_bundle import KPIAdderBundle
 
 
 class TestKPICalculatorPlugin(Plugin):
-    id = "force.bdss.kpi_calculators.test_kpi_calculator_plugin"
-
     kpi_calculators = List(
         IKPICalculatorBundle,
         contributes_to='force.bdss.kpi_calculators.bundles'
diff --git a/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py b/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py
index 3d534742f15abd2686f68a5752ec09cf9afcc725..0e376ed702075b8da638fbc76708a08863247622 100644
--- a/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py
+++ b/force_bdss/core_plugins/test_mco/dakota/dakota_bundle.py
@@ -3,6 +3,7 @@ from traits.trait_types import String
 
 from force_bdss.core_plugins.test_mco.dakota.dakota_communicator import \
     DakotaCommunicator
+from force_bdss.id_generators import bundle_id
 from force_bdss.mco.i_multi_criteria_optimizer_bundle import (
     IMultiCriteriaOptimizerBundle)
 
@@ -12,7 +13,7 @@ from .dakota_optimizer import DakotaOptimizer
 
 @provides(IMultiCriteriaOptimizerBundle)
 class DakotaBundle(HasStrictTraits):
-    name = String("dakota")
+    id = String(bundle_id("enthought", "dakota"))
 
     def create_model(self, model_data=None):
         if model_data is None:
diff --git a/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py b/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py
index a71da0114b74f46e60f2c400a090fd51ccfa4f98..7805023cf713beae114728718e82f23b96c96552 100644
--- a/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py
+++ b/force_bdss/core_plugins/test_mco/multi_criteria_optimizers_plugin.py
@@ -8,8 +8,6 @@ from .dakota.dakota_bundle import DakotaBundle
 
 
 class MultiCriteriaOptimizersPlugin(Plugin):
-    id = "force.bdss.mco.plugins.multi_criteria_optimizers_plugin"
-
     multi_criteria_optimizers = List(
         IMultiCriteriaOptimizerBundle,
         contributes_to='force.bdss.mco.bundles'
diff --git a/force_bdss/data_sources/i_data_source_bundle.py b/force_bdss/data_sources/i_data_source_bundle.py
index 5db54c836efe5da59391eb4db5711f7dad973913..146c1e8cbaa03be765ad68d2707411b58046824c 100644
--- a/force_bdss/data_sources/i_data_source_bundle.py
+++ b/force_bdss/data_sources/i_data_source_bundle.py
@@ -2,6 +2,11 @@ from traits.api import Interface, String
 
 
 class IDataSourceBundle(Interface):
+    #: Unique identifier that identifies the bundle uniquely in the
+    #: universe of bundles. Create one with the function bundle_id()
+    id = String()
+
+    #: A human readable name of the bundle
     name = String()
 
     def create_data_source(self, application, model):
diff --git a/force_bdss/id_generators.py b/force_bdss/id_generators.py
new file mode 100644
index 0000000000000000000000000000000000000000..d89eca0783ff33d4c0e1050dc59967b2771629d8
--- /dev/null
+++ b/force_bdss/id_generators.py
@@ -0,0 +1,28 @@
+import six
+
+
+def bundle_id(producer, identifier):
+    """Creates an id for the bundle.
+
+    Parameters
+    ----------
+    producer: str
+        the company or research institute unique identifier (e.g. "enthought")
+    identifier: str
+        A unique identifier for the bundle. The producer has authority and
+        control over the uniqueness of this identifier.
+
+    Returns
+    -------
+    str: an identifier to be used in the bundle.
+    """
+    def is_valid(entry):
+        return (
+            isinstance(entry, six.string_types) and
+            " " not in entry and
+            len(entry) != 0)
+
+    if not all(map(is_valid, [producer, identifier])):
+        raise ValueError("Invalid parameters specified.")
+
+    return "force.bdss.bundles.{}.{}".format(producer, identifier)
diff --git a/force_bdss/kpi/i_kpi_calculator_bundle.py b/force_bdss/kpi/i_kpi_calculator_bundle.py
index 42f780e5efff2c7f7908d79dc149894215bc143c..d4e8992ba059883f1b8824eb74966f3cdb0222e3 100644
--- a/force_bdss/kpi/i_kpi_calculator_bundle.py
+++ b/force_bdss/kpi/i_kpi_calculator_bundle.py
@@ -2,7 +2,7 @@ from traits.api import Interface, String
 
 
 class IKPICalculatorBundle(Interface):
-    name = String()
+    id = String()
 
     def create_kpi_calculator(self, application, model):
         pass
diff --git a/force_bdss/mco/i_multi_criteria_optimizer_bundle.py b/force_bdss/mco/i_multi_criteria_optimizer_bundle.py
index 69fe7a79a139ffbb48f71f0cd7f99d4cec8813ac..cb4222992cf0d7349a798e9747511c7c7e73b4d6 100644
--- a/force_bdss/mco/i_multi_criteria_optimizer_bundle.py
+++ b/force_bdss/mco/i_multi_criteria_optimizer_bundle.py
@@ -2,7 +2,7 @@ from traits.api import Interface, String
 
 
 class IMultiCriteriaOptimizerBundle(Interface):
-    name = String()
+    id = String()
 
     def create_optimizer(self, application, model):
         pass
diff --git a/force_bdss/tests/__init__.py b/force_bdss/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/force_bdss/tests/test_id_generators.py b/force_bdss/tests/test_id_generators.py
new file mode 100644
index 0000000000000000000000000000000000000000..85d240aaada5511a3de3f775e0be42352d3fbe14
--- /dev/null
+++ b/force_bdss/tests/test_id_generators.py
@@ -0,0 +1,15 @@
+import unittest
+
+from force_bdss.id_generators import bundle_id
+
+
+class TestIdGenerators(unittest.TestCase):
+    def test_bundle_id(self):
+        self.assertEqual(bundle_id("foo", "bar"),
+                         "force.bdss.bundles.foo.bar")
+
+        for bad_entry in ["", None, "   ", "foo bar"]:
+            with self.assertRaises(ValueError):
+                bundle_id(bad_entry, "bar")
+            with self.assertRaises(ValueError):
+                bundle_id("foo", bad_entry)
diff --git a/force_bdss/workspecs/data_source.py b/force_bdss/workspecs/data_source.py
index 944e41ce68b6fa60fd9be2c77a6f651df2832b00..bad5752c63bdf9bb9b3d76256592d9ec875c53c4 100644
--- a/force_bdss/workspecs/data_source.py
+++ b/force_bdss/workspecs/data_source.py
@@ -1,15 +1,14 @@
-from traits.has_traits import HasStrictTraits
-from traits.trait_types import String, Dict
+from traits.api import HasStrictTraits, String, Dict
 
 
 class DataSource(HasStrictTraits):
-    name = String()
+    id = String()
     model_data = Dict()
 
     @classmethod
     def from_json(cls, json_data):
         self = cls(
-            name=json_data["name"],
+            id=json_data["id"],
             model_data=json_data["model_data"]
         )
 
diff --git a/force_bdss/workspecs/kpi_calculator.py b/force_bdss/workspecs/kpi_calculator.py
index ec41bbfa1b5f5e7dc083c84066e61440b2771810..b8066259538af6b8e19168d4404a432e3f2bdadc 100644
--- a/force_bdss/workspecs/kpi_calculator.py
+++ b/force_bdss/workspecs/kpi_calculator.py
@@ -1,15 +1,14 @@
-from traits.has_traits import HasStrictTraits
-from traits.trait_types import String, Dict
+from traits.api import HasStrictTraits, String, Dict
 
 
 class KPICalculator(HasStrictTraits):
-    name = String()
+    id = String()
     model_data = Dict()
 
     @classmethod
     def from_json(cls, json_data):
         self = cls(
-            name=json_data["name"],
+            id=json_data["id"],
             model_data=json_data["model_data"]
         )
 
diff --git a/force_bdss/workspecs/multi_criteria_optimizer.py b/force_bdss/workspecs/multi_criteria_optimizer.py
index 72a2fa6b264fb3f5cd0be4fc9af9fb284a6fca96..efa4bfd9428e7c8e6a4d093649b0fc07d79c81a8 100644
--- a/force_bdss/workspecs/multi_criteria_optimizer.py
+++ b/force_bdss/workspecs/multi_criteria_optimizer.py
@@ -2,13 +2,13 @@ from traits.api import HasStrictTraits, String, Dict
 
 
 class MultiCriteriaOptimizer(HasStrictTraits):
-    name = String()
+    id = String()
     model_data = Dict()
 
     @classmethod
     def from_json(cls, json_data):
         self = cls(
-            name=json_data["name"],
+            id=json_data["id"],
             model_data=json_data["model_data"]
         )