diff --git a/.drupalci.yml.example b/.drupalci.yml.example
index 7befa58..9ca7a03 100644
--- a/.drupalci.yml.example
+++ b/.drupalci.yml.example
@@ -2,7 +2,7 @@
 # To use:
 # - Drupal checkout in /tmp/drupal
 # - drupalci config:load blank
-# - drupalci config:set DCI_UseLocalCodeBase=/tmp/drupal
+# - drupalci config:set DCI_CodeBase=/tmp/drupal
 # - drupalci run simpletest
 # Must have php-5.4 container image
 environment:
diff --git a/src/DrupalCI/Console/Command/Config/ConfigLoadCommand.php b/src/DrupalCI/Console/Command/Config/ConfigLoadCommand.php
index c37b5f4..1da73db 100644
--- a/src/DrupalCI/Console/Command/Config/ConfigLoadCommand.php
+++ b/src/DrupalCI/Console/Command/Config/ConfigLoadCommand.php
@@ -17,9 +17,9 @@ use Symfony\Component\Console\Question\ChoiceQuestion;
 use Symfony\Component\Console\Question\ConfirmationQuestion;
 
 /**
- *   load <configset>  Clears all DrupalCI namespaced environment variables and
+ *   load <configset>  Clears all drupalci namespaced environment variables and
  *                       establishes new values matching the combination of
- *                       DrupalCI defaults and overrides from the chosen config
+ *                       drupalci defaults and overrides from the chosen config
  *                       set, as defined in ~/.drupalci/configs/<configset>.
  */
 class ConfigLoadCommand extends DrupalCICommandBase {
diff --git a/src/DrupalCI/Console/Command/Config/ConfigResetCommand.php b/src/DrupalCI/Console/Command/Config/ConfigResetCommand.php
index 8b44577..96e44bd 100644
--- a/src/DrupalCI/Console/Command/Config/ConfigResetCommand.php
+++ b/src/DrupalCI/Console/Command/Config/ConfigResetCommand.php
@@ -18,7 +18,7 @@ use Symfony\Component\Console\Question\ConfirmationQuestion;
 
 /**
  *   reset <setting>   Clears local default overrides by resetting the value of
- *                       the associated environment variable to the DrupalCI
+ *                       the associated environment variable to the drupalci
  *                       defaults.  Also supports 'ALL'.
  */
 class ConfigResetCommand extends DrupalCICommandBase {
diff --git a/src/DrupalCI/Console/Command/Config/ConfigShowCommand.php b/src/DrupalCI/Console/Command/Config/ConfigShowCommand.php
index 7e52fbc..bac0b41 100644
--- a/src/DrupalCI/Console/Command/Config/ConfigShowCommand.php
+++ b/src/DrupalCI/Console/Command/Config/ConfigShowCommand.php
@@ -19,7 +19,7 @@ use Symfony\Component\Console\Question\ChoiceQuestion;
  *   show <configset>  Outputs the testing default configuration overrides from
  *                       a given ~/.drupalci/configs/<configset> config set, or
  *                       if <configset> is not specified, the current
- *                       configuration (a combination of DrupalCI defaults,
+ *                       configuration (a combination of drupalci defaults,
  *                       config set overrides, and manual overrides established
  *                       via the 'set' command).
  */
diff --git a/src/DrupalCI/Console/Command/Init/InitConfigCommand.php b/src/DrupalCI/Console/Command/Init/InitConfigCommand.php
index 8fb20d2..b72d4ac 100644
--- a/src/DrupalCI/Console/Command/Init/InitConfigCommand.php
+++ b/src/DrupalCI/Console/Command/Init/InitConfigCommand.php
@@ -39,7 +39,7 @@ class InitConfigCommand extends DrupalCICommandBase {
     $output->writeln("<info>Executing init:config</info>");
 
     # Check whether ~/.drupalci directory exists, and force option not called
-    # TODO: Parameterize the DrupalCI directory
+    # TODO: Parameterize the drupalci directory
 
     $homedir = getenv('HOME');
 
@@ -85,7 +85,7 @@ class InitConfigCommand extends DrupalCICommandBase {
       // TODO: Currently using placeholder files.  Populate file contents.
       $finder = new Finder();
       $directory = "./configsets";
-      // TODO: This means we can only execute the command from the DrupalCI
+      // TODO: This means we can only execute the command from the drupalci
       // directory.  Need to be able to run from anywhere - determine how to
       // get the current script execution directory (not the /bin symlink!)
       // and construct an absolute directory path above.
diff --git a/src/DrupalCI/Console/Command/RunCommand.php b/src/DrupalCI/Console/Command/RunCommand.php
index 98bcba5..28b2a59 100644
--- a/src/DrupalCI/Console/Command/RunCommand.php
+++ b/src/DrupalCI/Console/Command/RunCommand.php
@@ -2,16 +2,13 @@
 
 /**
  * @file
- * Command class for Run.
+ * Command class for run.
  */
 
 namespace DrupalCI\Console\Command;
 
 use DrupalCI\Console\Helpers\ConfigHelper;
 use DrupalCI\Console\Output;
-use DrupalCI\Job\CodeBase\JobCodeBase;
-use DrupalCI\Job\Definition\JobDefinition;
-use DrupalCI\Job\Results\JobResults;
 use DrupalCI\Plugin\PluginManager;
 use Symfony\Component\Console\Input\InputInterface;
 use Symfony\Component\Console\Output\OutputInterface;
@@ -31,143 +28,140 @@ class RunCommand extends DrupalCICommandBase {
 
   /**
    * {@inheritdoc}
+   *
+   * Options:
+   *   Will probably be a combination of things taken from environment variables
+   *   and job specific options.
+   *   TODO: Sort out how to define job-specific options, and be able to import
+   *   them into the drupalci command. (Imported from a specially named file in
+   *   the job directory, perhaps?) Will need syntax to define required versus
+   *   optional options, and their defaults if not specified.
    */
   protected function configure() {
     $this
       ->setName('run')
       ->setDescription('Execute a given job run.')
-      // Argument may be the job type or a specific job definition file
       ->addArgument('definition', InputArgument::OPTIONAL, 'Job definition.');
+    // TODO: Add 'definition file name' as an option
   }
 
   /**
    * {@inheritdoc}
    */
   public function execute(InputInterface $input, OutputInterface $output) {
-    $arg = $input->getArgument('definition');
-
-    $config_helper = new ConfigHelper();
-    $local_overrides = $config_helper->getCurrentConfigSetParsed();
-
-    // Determine the Job Type based on the first argument to the run command
-    if ($arg) {
-      $job_type = (strtolower(substr(trim($arg), -4)) == ".yml") ? "generic" : trim($arg);
+    $definition = $input->getArgument('definition');
+    // The definition argument is optional, so we need to set a default definition file if it's not provided.
+    if (!$definition) {
+      // See if we've defined a default job type in our local configuration overrides
+      $confighelper = new ConfigHelper();
+      $local_overrides = $confighelper->getCurrentConfigSetParsed();
+      if (!empty($local_overrides['DCI_JobType'])) {
+        $definition = $local_overrides['DCI_JobType'];
+      }
+      else {
+        // Default to a drupalci.yml file in the local directory
+        $definition = "./drupalci.yml";
+      }
+    }
+    // Populate the job type and definition file variables
+    if (substr(trim($definition), -4) == ".yml") {
+      // "File" arguments
+      $job_type = 'generic';
+      $definition_file = $definition;
     }
     else {
-      // If no argument defined, then check for a default in the local overrides
-      $job_type = (!empty($local_overrides['DCI_JobType'])) ? $local_overrides['DCI_JobType'] : 'generic';
+      // "Job Type" arguments
+      $job_type = $definition;
+      $definition_file = __DIR__ . "/../../Plugin/JobTypes/$job_type/drupalci.yml";
     }
+    // TODO: Make sure $definition_file exists
 
-    // Load the associated class for this job type
     /** @var $job \DrupalCI\Plugin\JobTypes\JobInterface */
     $job = $this->jobPluginManager()->getPlugin($job_type, $job_type);
 
     // Link our $output variable to the job, so that jobs can display their work.
     Output::setOutput($output);
 
-    // Generate a unique job build_id, and store it within the job object
-    $job->generateBuildId();
-
-    // Create our job Codebase object and attach it to the job.
-    $job_codebase = new JobCodebase();
-    $job->setJobCodebase($job_codebase);
+    // Store the definition file argument in the job so we can act on it later
+    $job->setDefinitionFile($definition_file);
 
-    // Create our job Definition object and attach it to the job.
-    $job_definition = new JobDefinition();
-    $job->setJobDefinition($job_definition);
-
-    // Compile our complete list of DCI_* variables
-    $job_definition->compile($job);
-
-    // Setup our project and version metadata
-    $job_codebase->setupProject($job_definition);
-
-    // Determine the job definition template to be used
-    if ($arg && strtolower(substr(trim($arg), -4)) == ".yml") {
-      $template_file = $arg;
+    // Create a unique job build_id
+    // Check for BUILD_TAG environment variable, and if not present, create a random result.
+    $build_id = getenv('BUILD_TAG');
+    if (empty($build_id)) {
+      $build_id = $job_type . '_' . time();
     }
-    else {
-      $template_file = $job->getDefaultDefinitionTemplate($job_type);
-    }
-
-    Output::writeLn("<info>Using job definition template: <options=bold>$template_file</options=bold></info>");
 
-    // Load our job template file into the job definition.  If $template_file
-    // doesn't exist, this will trigger a FileNotFound or ParseError exception.
-    $job_definition->loadTemplateFile($template_file);
+    $job->setBuildId($build_id);
 
-    // Process the complete job definition, taking into account DCI_* variable
-    // and definition preprocessors, along with job-specific arguments
-    $job_definition->preprocess($job);
-
-    // Validate the resulting job definition, to ensure all required parameters
-    // are present.
-    $result = $job_definition->validate($job);
-    if (!$result) {
-      // Job definition failed validation.  Error output has already been
-      // generated and displayed during execution of the validation method.
-      return;
+    // Load the job definition, environment defaults, and any job-specific configuration steps which need to occur
+    // TODO: Add prep_results once results API integration is complete
+    foreach (['compile_definition', 'validate_definition', 'setup_directories', 'prepare_results_placeholders'] as $step) {
+    // foreach (['compile_definition', 'validate_definition', 'setup_directories'] as $step) {
+      $this->buildstepsPluginManager()->getPlugin('configure', $step)->run($job, NULL);
     }
 
-    // Set up the local working directory
-    $result = $job_codebase->setupWorkingDirectory($job_definition);
-    if ($result === FALSE) {
-      // Error encountered while setting up the working directory. Error output
-      // has already been generated and displayed during execution of the
-      // setupWorkingDirectory method.
+    if ($job->getErrorState()) {
+      $output->writeln("<error>Job halted due to an error while configuring job.</error>");
       return;
     }
 
-    // Create our job Results object and attach it to the job.
-    $job_results = new JobResults($job);
-    $job->setJobResults($job_results);
-
     // The job should now have a fully merged job definition file, including
-    // any local or DrupalCI defaults not otherwise defined in the passed job
-    // definition
-    $definition = $job_definition->getDefinition();
-
-    // Iterate over the build stages
-    foreach ($definition as $build_stage => $steps) {
-      if (empty($steps)) {
-        $job_results->updateStageStatus($build_stage, 'Skipped');
-        continue;
-      }
-      $job_results->updateStageStatus($build_stage, 'Executing');
+    // any local or drupalci defaults not otherwise defined in the passed job
+    // definition, located in $job->job_definition
+    $definition = $job->getDefinition();
+    if (!empty($definition['publish']['drupalci_results'])) {
+      $results_data = $definition['publish']['drupalci_results'];
+      // $data format:
+      // i) array('config' => '<configuration filename>'),
+      // ii) array('host' => '...', 'username' => '...', 'password' => '...')
+      // or a mixed array of the above
+      // iii) array(array(...), array(...))
+      // Normalize data to the third format, if necessary
+      $results_data = (count($results_data) == count($results_data, COUNT_RECURSIVE)) ? [$results_data] : $results_data;
+    }
+    else {
+      $results_data = array();
+    }
 
-      // Iterate over the build steps
-      foreach ($steps as $build_step => $data) {
-        $job_results->updateStepStatus($build_stage, $build_step, 'Executing');
-        // Execute the build step
-        $this->buildStepsPluginManager()->getPlugin($build_stage, $build_step)->run($job, $data);
+    foreach ($definition as $build_step => $step) {
+      if (empty($step)) { continue; }
+      // If we are publishing this job to a results server (or multiple), update the progress on the server(s)
+      // TODO: Check current state, and don't progress if already there.
+      foreach ($results_data as $key => $instance) {
+        $job->configureResultsAPI($instance);
+        $api = $job->getResultsAPI();
+        $url = $api->getUrl();
+        // Retrieve the results node ID for the results server
+        $host = parse_url($url, PHP_URL_HOST);
+        $states = $api->states();
+        $results_id = $job->getResultsServerID();
+
+        foreach ($states as $key => $state) {
+          if ($build_step == $key) {
+            $api->progress($results_id[$host], $state['id']);
+            break;
+          }
+        }
+      }
 
-        // Check for errors / failures after build step execution
-        $status = $job_results->getResultByStep($build_stage, $build_step);
-        if ($status == 'Error') {
+      foreach ($step as $plugin => $data) {
+        $this->buildstepsPluginManager()->getPlugin($build_step, $plugin)->run($job, $data);
+        if ($job->getErrorState()) {
           // Step returned an error.  Halt execution.
-          Output::error("Execution Error", "Error encountered while executing job build step <options=bold>$build_stage:$build_step</options=bold>");
+          // TODO: Graceful handling of early exit states.
+          $output->writeln("<error>Job halted.</error>");
+          $output->writeln("<comment>Exiting job due to an invalid return code during job build step: <options=bold>'$build_step=>$plugin'</options=bold></comment>");
           break 2;
         }
-        if ($status == 'Fail') {
-          // Step returned an failure.  Halt execution.
-          Output::error("Execution Failure", "Build step <options=bold>$build_stage:$build_step</options=bold> FAILED");
-          break 2;
-        }
-        $job_results->updateStepStatus($build_stage, $build_step, 'Completed');
       }
-      $job_results->updateStageStatus($build_stage, 'Completed');
     }
-    // TODO: Gather results.
-    // This should be moved out of the 'build steps' logic, as an error in any
-    // build step halts execution of the entire loop, and the artifacts are not
-    // processed.
-
   }
 
   /**
    * @return \DrupalCI\Plugin\PluginManagerInterface
    */
-  protected function buildStepsPluginManager() {
+  protected function buildstepsPluginManager() {
     if (!isset($this->buildStepsPluginManager)) {
       $this->buildStepsPluginManager = new PluginManager('BuildSteps');
     }
diff --git a/src/DrupalCI/Console/Helpers/ContainerHelper.php b/src/DrupalCI/Console/Helpers/ContainerHelper.php
index 2e29c54..61e0c91 100644
--- a/src/DrupalCI/Console/Helpers/ContainerHelper.php
+++ b/src/DrupalCI/Console/Helpers/ContainerHelper.php
@@ -17,7 +17,7 @@ class ContainerHelper extends DrupalCIHelperBase {
    * {@inheritdoc}
    */
   public function getContainers($type){
-    // TODO: Make sure we're starting from the DrupalCI root
+    // TODO: Make sure we're starting from the drupalci root
     $option = array();
     $containers = glob('containers/'.$type.'/*', GLOB_ONLYDIR);
     foreach ($containers as $container) {
diff --git a/src/DrupalCI/Job/Artifacts/BuildArtifact.php b/src/DrupalCI/Job/Artifacts/BuildArtifact.php
new file mode 100644
index 0000000..dcff970
--- /dev/null
+++ b/src/DrupalCI/Job/Artifacts/BuildArtifact.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \DrupalCI\Job\Artifacts\BuildArtifact.
+ */
+
+namespace DrupalCI\Job\Artifacts;
+
+/**
+ * Class BuildArtifact
+ * @package DrupalCI\Job\Artifacts
+ *
+ * Defines a build artifact for a given job.
+ */
+class BuildArtifact {
+
+  // Valid build artifact types include file, directory, or string.
+  protected $type;
+  public function setType($type) { $this->type = $type; }
+  public function getType() { return $this->type; }
+
+  // Value contains the file/directory location, or the actual string content.
+  protected $value;
+  public function setValue($value) { $this->value = $value; }
+  public function getValue() { return $this->value; }
+
+  public function __construct($type, $value) {
+    $this->setType($type);
+    $this->setValue($value);
+  }
+
+}
diff --git a/src/DrupalCI/Job/Artifacts/BuildArtifactList.php b/src/DrupalCI/Job/Artifacts/BuildArtifactList.php
new file mode 100644
index 0000000..44fef9a
--- /dev/null
+++ b/src/DrupalCI/Job/Artifacts/BuildArtifactList.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \DrupalCI\Job\Artifacts\BuildArtifactList.
+ */
+
+namespace DrupalCI\Job\Artifacts;
+
+/**
+ * Class BuildArtifactList
+ * @package DrupalCI\Job\Artifacts
+ *
+ * Contains a list of Build Artifacts relevant for a given job.
+ */
+class BuildArtifactList {
+
+  /**
+   * array of \DrupalCI\Job\Artifacts\BuildArtifact objects
+   */
+  protected $artifacts = array();
+
+  public function addArtifact($key, $artifact) {
+      $this->artifacts[$key] = $artifact;
+  }
+
+  public function removeArtifact($key) {
+    if (isset($this->artifacts[$key])) {
+      unset($this->artifacts[$key]);
+    }
+  }
+
+  public function getArtifact($key) {
+    if (isset($this->artifacts[$key])) {
+      return $this->artifacts[$key];
+    }
+    else {
+      // TODO: Error Handling
+    }
+  }
+
+  public function getArtifacts() {
+    return $this->artifacts;
+  }
+
+  public function getKeys() {
+    return array_keys($this->artifacts);
+  }
+
+  public function length() {
+    return count($this->artifacts);
+  }
+
+  public function keyExists($key) {
+    return isset($this->artifacts[$key]);
+  }
+
+}
diff --git a/src/DrupalCI/Job/CodeBase/JobCodeBase.php b/src/DrupalCI/Job/CodeBase/JobCodeBase.php
deleted file mode 100644
index e43c91b..0000000
--- a/src/DrupalCI/Job/CodeBase/JobCodeBase.php
+++ /dev/null
@@ -1,187 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \DrupalCI\Job\CodeBase\JobCodebase
- */
-
-namespace DrupalCI\Job\CodeBase;
-
-use DrupalCI\Console\Output;
-use DrupalCI\Job\Definition\JobDefinition;
-use DrupalCI\Plugin\JobTypes\JobInterface;
-
-class JobCodebase {
-
-  /**
-   * The base working directory for this codebase build
-   *
-   * @var string
-   */
-  protected $working_dir;
-  public function setWorkingDir($working_dir) {  $this->working_dir = $working_dir;  }
-  public function getWorkingDir() {  return $this->working_dir;  }
-
-  /**
-   * The core project for this job (e.g. Drupal)
-   *
-   * @var string
-   */
-  protected $core_project;
-  public function getCoreProject()  {  return $this->core_project;  }
-  public function setCoreProject($core_project) { $this->core_project = $core_project; }
-
-  /**
-   * The specific version of the core project (e.g. 8.0.x)
-   *
-   * @var string
-   */
-  protected $core_version;
-  public function getCoreVersion() {  return $this->core_version;  }
-  public function setCoreVersion($core_version) {  $this->core_version = $core_version;  }
-
-  /**
-   * The major version of the core project (e.g. 8)
-   *
-   * @var string
-   */
-  protected $core_major_version;
-  public function getCoreMajorVersion() {  return $this->core_major_version;  }
-  public function setCoreMajorVersion($core_major_version) {  $this->core_major_version = $core_major_version;  }
-
-  /**
-   * The repositories used to generate this codebase
-   *
-   * @var \DrupalCI\Job\CodeBase\Repository
-   */
-  protected $repositories;
-  public function setRepositories($repositories) {  $this->repositories = $repositories;  }
-  public function getRepositories() {  return $this->repositories;  }
-  public function addRepository(Repository $repository) {
-    $repositories = $this->getRepositories();
-    $repositories[] = $repository;
-    $this->setRepositories($repository);
-  }
-
-  public function setupProject(JobDefinition $job_definition) {
-    // Core Project
-    // For future compatibility.  In the future, we could potentially add
-    // project specific plugins, in which case users should pass the project
-    // name in using DCI_CoreProject. This will allow plugins to reference
-    // the core project using $job->getCodebase()->getCoreProject().
-    $core_project = $job_definition->getDCIVariable('DCI_CoreProject') ?: 'generic';
-    $this->setCoreProject($core_project);
-
-    // Core Version and Major Version
-    // The default job templates, run commands, and other script requirements
-    // may vary depending on core project version.  For example, the simpletest
-    // test execution script resides a different paths in Drupal 8 than Drupal7
-    $version = $this->determineVersion($job_definition);
-    if (!empty($version)) {
-      $this->setCoreVersion($version);
-      $this->setCoreMajorVersion($this->determineMajorVersion($version));
-    }
-    else {
-      // Unable to determine core project version. We'll let this go for now,
-      // to allow other plugins to set this later down the line; but this
-      // means that any code operating on the core version needs to be able to
-      // accommodate the 'no version set' case on it's own.
-    }
-  }
-
-  protected function determineVersion(JobDefinition $job_definition) {
-    // It may not always be possible to determine the core project version, but
-    // we can make a reasonable guess.
-    // Option 1: Use the user-supplied core version, if one exists.
-    if ($version = $job_definition->getDCIVariable('DCI_CoreVersion')) {
-      return $version;
-    }
-    // Option 2: Try to deduce it based on the supplied core branch
-    elseif ($version = $job_definition->getDCIVariable('DCI_CoreBranch')) {
-      // Define our preg_match patterns
-      $drupal_pattern = "/^((\d+)\.(\d+|x)(?:\.(\d+|x))?(?:(?:\-)?(?:alpha|beta|rc|dev)(?:\.)?(\d+)?)?)$/";
-      $semantic_pattern = "/^((?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+))/";
-      // Check if the branch matches Drupal branch naming patterns
-      if (preg_match($drupal_pattern, $version, $matches) !== 0) {
-        return $matches[0];
-      }
-      // Check if the branch matches semantic versioning
-      elseif (preg_match($semantic_pattern, $version, $matches) !== 0) {
-        return $matches[0];
-      }
-    }
-    return NULL;
-  }
-
-  protected function determineMajorVersion($version) {
-    $pattern = "/^(\d+)/";
-    if (preg_match($pattern, $version, $matches)) {
-      return $matches[0];
-    }
-    return NULL;
-  }
-
-  /**
-   * Initialize Codebase
-   */
-  public function setupWorkingDirectory(JobDefinition $job_definition) {
-    // Check if the target working directory has been specified.
-    $working_dir = $job_definition->getDCIVariable('DCI_WorkingDir');
-    $tmp_directory = sys_get_temp_dir();
-
-    // Generate a default directory name if none specified
-    if (empty($working_dir)) {
-      // Case:  No explicit working directory defined.
-      $working_dir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $job_definition->getDCIVariable('DCI_JobBuildId');
-    }
-    else {
-      // We force the working directory to always be under the system temp dir.
-      if (strpos($working_dir, realpath($tmp_directory)) !== 0) {
-        if (substr($working_dir, 0, 1) == DIRECTORY_SEPARATOR) {
-          $working_dir = $tmp_directory . $working_dir;
-        }
-        else {
-          $working_dir = $tmp_directory . DIRECTORY_SEPARATOR . $working_dir;
-        }
-      }
-    }
-    // Create directory if it doesn't already exist
-    if (!is_dir($working_dir)) {
-      $result = mkdir($working_dir, 0777, TRUE);
-      if (!$result) {
-        // Error creating checkout directory
-        Output::error('Directory Creation Error', 'Error encountered while attempting to create local working directory');
-        return FALSE;
-      }
-      Output::writeLn("<info>Checkout directory created at <options=bold>$working_dir</options=bold></info>");
-    }
-
-    // Validate that the working directory is empty.  If the directory contains
-    // an existing git repository, for example, our checkout attempts will fail
-    // TODO: Prompt the user to ask if they'd like to overwrite
-    $iterator = new \FilesystemIterator($working_dir);
-    if ($iterator->valid()) {
-      // Existing files found in directory.
-      Output::error('Directory not empty', 'Unable to use a non-empty working directory.');
-      return FALSE;
-    };
-
-    // Convert to the full path and ensure our directory is still valid
-    $working_dir = realpath($working_dir);
-    if (!$working_dir) {
-      // Directory not found after conversion to canonicalized absolute path
-      Output::error('Directory not found', 'Unable to determine working directory absolute path.');
-      return FALSE;
-    }
-
-    // Ensure we're still within the system temp directory
-    if (strpos(realpath($working_dir), realpath($tmp_directory)) !== 0) {
-      Output::error('Directory error', 'Detected attempt to traverse out of the system temp directory.');
-      return FALSE;
-    }
-
-    // If we arrive here, we have a valid empty working directory.
-    $this->setWorkingDir($working_dir);
-    $job_definition->setDCIVariable('DCI_WorkingDir', $working_dir);
-  }
-}
\ No newline at end of file
diff --git a/src/DrupalCI/Job/Definition/JobDefinition.php b/src/DrupalCI/Job/Definition/JobDefinition.php
index 306b4ac..12e1f5f 100644
--- a/src/DrupalCI/Job/Definition/JobDefinition.php
+++ b/src/DrupalCI/Job/Definition/JobDefinition.php
@@ -5,340 +5,52 @@
  * Contains \DrupalCI\Job\Definition\JobDefinition.
  */
 
-namespace DrupalCI\Job\Definition;
+// TODO: This class does not appear to ever be called
 
-use DrupalCI\Console\Helpers\ConfigHelper;
-use DrupalCI\Console\Output;
-use DrupalCI\Plugin\JobTypes\JobInterface;
-use DrupalCI\Plugin\PluginManager;
-use Symfony\Component\Filesystem\Exception\FileNotFoundException;
-use Symfony\Component\Yaml\Exception\ParseException;
-use Symfony\Component\Yaml\Yaml;
+namespace DrupalCI\Console\Jobs\Definition;
 
-class JobDefinition {
-
-  // Location of our job definition template
-  protected $template_file;
-  protected function setTemplateFile($template_file) {  $this->template_file = $template_file; }
-
-  // Contains our array of DCI_* variables
-  protected $dci_variables;
-  public function getDCIVariables() {  return $this->dci_variables;  }
-  public function setDCIVariables($dci_variables) {  $this->dci_variables = $dci_variables;  }
-  public function setDCIVariable($dci_variable, $value) {  $this->dci_variables[$dci_variable] = $value;  }
-  public function getDCIVariable($dci_variable) {
-    return (!empty($this->dci_variables[$dci_variable])) ? $this->dci_variables[$dci_variable] : NULL;
-  }
-
-  // Contains the parsed job definition
-  protected $definition = array();
-  public function getDefinition() {  return $this->definition;  }
-  protected function setDefinition(array $job_definition) {  $this->definition = $job_definition;  }
-
-  // Contains the array of build steps
-  protected $build_steps = array();
-  public function getBuildSteps() {  return $this->build_steps;  }
-  protected function setBuildSteps(array $build_steps) {  $this->build_steps = $build_steps;  }
+use Symfony\Component\Yaml\Parser;
 
-  /**
-   * @var \DrupalCI\Plugin\PluginManager;
-   */
-  protected $pluginManager;
-
-  public function loadTemplateFile($template_file) {
-    // TODO: Pass in Job instead of template file, and calculate what template file we need as a result
-
-    // Store the template location
-    $this->setTemplateFile($template_file);
-
-    // Get and parse the default definition template (containing %DCI_*%
-    // placeholders) into the job definition.
-
-    // For 'generic' jobs, this is either the file passed in on the
-    // 'drupalci run <filename>' command; and should be fully populated (though
-    // template placeholders *can* be supported) ... or a drupalci.yml file at
-    // the working directory root.
-
-    // For other 'jobtype' jobs, this is the file location returned by
-    // the $job->getDefaultDefinitionTemplate() method, which defaults to
-    // DrupalCI/Plugin/JobTypes/<jobtype>/drupalci.yml for most job types.
+class JobDefinition {
 
-    if (!file_exists($template_file)) {
-      //Output::writeln("Unable to locate job definition template at <options=bold>$template_file</options=bold>");
-      throw new FileNotFoundException("Unable to locate job definition template at $template_file.");
-    }
+  // The definition source may be a local file or URL
+  protected $source = NULL;
 
-    // Attempt to parse the job definition template and save it to our definition variable.
-    // The YAML class will throw an exception if this fails.
-    $this->setDefinition($this->loadYaml($template_file));
+  public function setSource($filename) {
+    $this->source = $filename;
   }
 
-  /**
-   * Compile the complete list of DCI_* variables
-   */
-  public function compile(JobInterface $job) {
-    // Compile our list of DCI_* variables
-    $this->compileDciVariables($job);
+  public function getSource() {
+    return $this->source;
   }
 
-  /**
-   * Populates the job definition template based on DCI_* variables and
-   * job-specific arguments
-   */
-  public function preprocess(JobInterface $job) {
-    // Execute variable preprocessor plugin logic
-    $this->executeVariablePreprocessors();
-    // Execute definition preprocessor plugin logic
-    $this->executeDefinitionPreprocessors();
-    // Process DCI_* variable substitution into the job definition template
-    $this->substituteTemplateVariables();
-    // Add the build variables and job definition to our job object, for
-    // compatibility.
-    $job->setBuildVars($this->getDCIVariables() + $job->getBuildVars());
-    // Split out the final array of build steps into it's own element and store
-    // it for future use.
-    $this->setBuildSteps($this->parseBuildSteps());
-  }
+  // Placeholder for parsed key=>value parameter pairs
+  protected $parameters = array();
 
-  /**
-   * Validate that the job contains all required elements defined in the class
-   */
-  public function validate(JobInterface $job) {
-    // TODO: Ensure that all 'required' arguments are defined
-    $definition = $this->getDefinition();
-    $failflag = FALSE;
-    foreach ($job->getRequiredArguments() as $env_var => $yaml_loc) {
-      if (!empty($job->getBuildVars()[$env_var])) {
-        continue;
-      }
-      else {
-        // Look for the appropriate array structure in the job definition file
-        // eg: environment:db
-        $keys = explode(":", $yaml_loc);
-        $eval = $definition;
-        foreach ($keys as $key) {
-          if (!empty($eval[$key])) {
-            // Check if the next level contains a numeric [0] key, indicating a
-            // nested array of parameters.  If found, skip this level of the
-            // array.
-            if (isset($eval[$key][0])) {
-              $eval = $eval[$key][0];
-            }
-            else {
-              $eval=$eval[$key];
-            }
-          }
-          else {
-            // Missing a required key in the array key chain
-            $failflag = TRUE;
-            break;
-          }
-        }
-        if (!$failflag) {
-          continue;
-        }
-      }
-      // If processing gets to here, we're missing a required variable
-      Output::error("Invalid Job Definition", "Required test parameter <options=bold>'$env_var'</options=bold> not found in environment variables, and <options=bold>'$yaml_loc'</options=bold> not found in job definition file.");
-      // TODO: Graceful handling of failed exit states
-      return FALSE;
+  public function load() {
+    $source = $this->source;
+    if (empty($source)) {
+      // TODO: Throw exception
+      return;
     }
-    // TODO: Strip out arguments which are not defined in the 'Available' arguments array
-    return TRUE;
-  }
-
-  /**
-   * Given a file, returns an array containing the parsed YAML contents from that file
-   *
-   * @param $source
-   *   A YAML source file
-   * @return array
-   *   an array containing the parsed YAML contents from the source file
-   * @throws ParseException
-   */
-  protected function loadYaml($source) {
+    $yaml = new Parser();
     if ($content = file_get_contents($source)) {
-      return Yaml::parse($content);
-    }
-    throw new ParseException("Unable to parse empty job definition template file at $source.");
-  }
-
-  /**
-   * Compiles the list of available DCI_* variables to consider with this job
-   */
-  protected function compileDciVariables(JobInterface $job) {
-    // Get and parse external (i.e. anything not from the default definition
-    // file) job argument parameters.  DrupalCI jobs are controlled via a
-    // hierarchy of configuration settings, which define the behaviour of the
-    // platform while running DrupalCI jobs.  This hierarchy is defined as
-    // follows, which each level overriding the previous:
-
-    // 1. Out-of-the-box DrupalCI platform defaults, as defined in DrupalCI/Plugin/JobTypes/JobBase->platformDefaults
-    $platform_defaults = $job->getPlatformDefaults();
-    if (!empty($platform_defaults)) {
-      Output::writeLn("<comment>Loading DrupalCI platform default arguments:</comment>");
-      Output::writeLn(implode(",", array_keys($platform_defaults)));
-    }
-
-    // 2. Out-of-the-box DrupalCI JobType defaults, as defined in DrupalCI/Plugin/JobTypes/<jobtype>->defaultArguments
-    $jobtype_defaults = $job->getDefaultArguments();
-    if (!empty($jobtype_defaults)) {
-      Output::writeLn("<comment>Loading job type default arguments:</comment>");
-      Output::writeLn(implode(",", array_keys($jobtype_defaults)));
-    }
-
-    // 3. Local overrides defined in ~/.drupalci/config
-    $confighelper = new ConfigHelper();
-    $local_overrides = $confighelper->getCurrentConfigSetParsed();
-    if (!empty($local_overrides)) {
-      Output::writeLn("<comment>Loading local DrupalCI environment config override arguments.</comment>");
-      Output::writeLn(implode(",", array_keys($local_overrides)));
+      $parameters = $yaml->parse($content);
     }
-
-    // 4. 'DCI_' namespaced environment variable overrides
-    $environment_variables = $confighelper->getCurrentEnvVars();
-    if (!empty($environment_variables)) {
-      Output::writeLn("<comment>Loading local namespaced environment variable override arguments.</comment>");
-      Output::writeLn(implode(",", array_keys($environment_variables)));
-    }
-
-    // 5. Additional variables passed in via the command line
-    // TODO: Not yet implemented
-    $cli_variables = ['DCI_JobBuildId' => $job->getBuildId()];
-
-    // Combine the above to generate the final array of DCI_* key=>value pairs
-    $dci_variables = $cli_variables + $environment_variables + $local_overrides + $jobtype_defaults + $platform_defaults;
-
-    // Reorder array, placing priority variables at the front
-    if (!empty($job->priorityArguments)) {
-      $original_array = $dci_variables;
-      $original_keys = array_keys($original_array);
-      $ordered_variables = [];
-      foreach ($job->priorityArguments as $element) {
-        if (in_array($element, $original_keys)) {
-          $ordered_variables[$element] = $original_array[$element];
-          unset($original_array[$element]);
-        }
-      }
-      $dci_variables = array_merge($ordered_variables, $original_array);
-    }
-
-    $this->setDCIVariables($dci_variables);
-  }
-
-  /**
-   * Execute Variable preprocessor Plugin logic
-   */
-  protected function executeVariablePreprocessors() {
-    // For each DCI_* element in the array, check to see if a variable
-    // preprocessor exists, and process it if it does.
-    $replacements = [];
-    $dci_variables = $this->getDCIVariables();
-    $plugin_manager = $this->getPreprocessPluginManager();
-    foreach ($dci_variables as $key => &$value) {
-      if (preg_match('/^DCI_(.+)$/i', $key, $matches)) {
-        $name = strtolower($matches[1]);
-        if ($plugin_manager->hasPlugin('variable', $name)) {
-          /** @var \DrupalCI\Plugin\Preprocess\VariableInterface $plugin */
-          $plugin = $plugin_manager->getPlugin('variable', $name);
-          // @TODO: perhaps this should be on the annotation.
-          $new_keys = $plugin->target();
-          if (!is_array($new_keys)) {
-            $new_keys = [$new_keys];
-          }
-          // @TODO: error handling.
-          foreach ($new_keys as $new_key) {
-            // Only process variable plugins if the variable being changed actually exists.
-            if (!empty($dci_variables[$new_key])) {
-              $dci_variables[$new_key] = $plugin->process($dci_variables[$new_key], $value, $new_key);
-            }
-          }
-        }
-      }
-    }
-    $this->setDCIVariables($dci_variables);
-  }
-
-  /**
-   * Execute Variable preprocessor Plugin logic
-   */
-  protected function executeDefinitionPreprocessors() {
-    $definition = $this->getDefinition();
-    $dci_variables = $this->getDCIVariables();
-    $plugin_manager = $this->getPreprocessPluginManager();
-    // Foreach DCI_* pair in the array, check if a definition plugin exists,
-    // and process if it does.  We pass in the test definition template and
-    // complete array of DCI_* variables.
-    foreach ($dci_variables as $key => $value) {
-      if (preg_match('/^DCI_(.+)$/', $key, $matches)) {
-        $name = strtolower($matches[1]);
-        if ($plugin_manager->hasPlugin('definition', $name)) {
-          $plugin_manager->getPlugin('definition', $name)
-            ->process($definition, $value, $dci_variables);
-        }
-      }
+    else {
+      // TODO: Error Handling
+      return -1;
     }
-    $this->setDefinition($definition);
+    $this->parameters = $parameters;
+    return;
   }
 
-  /**
-   * Substitute DCI_* variables into the job definition template
-   */
-  protected function substituteTemplateVariables() {
-    // Generate our replacements array
-    $replacements = [];
-    $dci_variables = $this->getDCIVariables();
-    foreach ($dci_variables as $key => $value) {
-      if (preg_match('/^DCI_(.+)$/', $key, $matches)) {
-        $name = strtolower($matches[1]);
-        $replacements["%$key%"] = $value;
-      }
-    }
-
-    // Add support for substituting '%HOME%' with the $HOME env variable
-    $replacements["%HOME%"] = getenv("HOME");
-
-    // Process DCI_* variable substitution into test definition template
-    $search = array_keys($replacements);
-    $replace = array_values($replacements);
-    $definition = $this->getDefinition();
-    array_walk_recursive($definition, function (&$value) use ($search, $replace) {
-      $value = str_ireplace($search, $replace, $value);
-    });
-
-    // Save our post-replacements job definition back to the object
-    $this->setDefinition($definition);
-  }
-
-  protected function parseBuildSteps() {
-    $definition = $this->getDefinition();
-    $build_steps = [];
-    foreach ($definition as $stage => $steps) {
-      $build_steps[$stage] = [];
-      if (!empty($steps)) {
-        foreach ($steps as $step => $data) {
-          $build_steps[$stage][$step] = "";
-        }
-      }
-    }
-    return $build_steps;
+  public function getParameters() {
+    return $this->parameters;
   }
 
-
-  /**
-   * @return \DrupalCI\Plugin\PluginManager
-   */
-  protected function getPreprocessPluginManager() {
-    if (!isset($this->pluginManager)) {
-      $this->pluginManager = new PluginManager('Preprocess');
-    }
-    return $this->pluginManager;
+  public function setParameters(array $parameters) {
+    $this->parameters = $parameters;
   }
 
-
-
-  // Other potential methods for this class:
-  // insert build step before/after
-  // get/set DCI_parameters
-
 }
\ No newline at end of file
diff --git a/src/DrupalCI/Job/Results/Artifacts/BuildArtifact.php b/src/DrupalCI/Job/Results/Artifacts/BuildArtifact.php
deleted file mode 100644
index 9a36d79..0000000
--- a/src/DrupalCI/Job/Results/Artifacts/BuildArtifact.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \DrupalCI\Job\Artifacts\BuildArtifact.
- */
-
-namespace DrupalCI\Job\Results\Artifacts;
-
-/**
- * Class BuildArtifact
- * @package DrupalCI\Job\Artifacts
- *
- * Defines a build artifact for a given job.
- */
-class BuildArtifact {
-
-  // Valid build artifact types include file, directory, or string.
-  protected $type;
-  public function setType($type) { $this->type = $type; }
-  public function getType() { return $this->type; }
-
-  // Value contains the file/directory location, or the actual string content.
-  protected $value;
-  public function setValue($value) { $this->value = $value; }
-  public function getValue() { return $this->value; }
-
-  public function __construct($type, $value) {
-    $this->setType($type);
-    $this->setValue($value);
-  }
-
-}
diff --git a/src/DrupalCI/Job/Results/Artifacts/BuildArtifactList.php b/src/DrupalCI/Job/Results/Artifacts/BuildArtifactList.php
deleted file mode 100644
index 6ab92d8..0000000
--- a/src/DrupalCI/Job/Results/Artifacts/BuildArtifactList.php
+++ /dev/null
@@ -1,58 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \DrupalCI\Job\Artifacts\BuildArtifactList.
- */
-
-namespace DrupalCI\Job\Results\Artifacts;
-
-/**
- * Class BuildArtifactList
- * @package DrupalCI\Job\Artifacts
- *
- * Contains a list of Build Artifacts relevant for a given job.
- */
-class BuildArtifactList {
-
-  /**
-   * array of \DrupalCI\Job\Artifacts\BuildArtifact objects
-   */
-  protected $artifacts = array();
-
-  public function addArtifact($key, $artifact) {
-      $this->artifacts[$key] = $artifact;
-  }
-
-  public function removeArtifact($key) {
-    if (isset($this->artifacts[$key])) {
-      unset($this->artifacts[$key]);
-    }
-  }
-
-  public function getArtifact($key) {
-    if (isset($this->artifacts[$key])) {
-      return $this->artifacts[$key];
-    }
-    else {
-      // TODO: Error Handling
-    }
-  }
-
-  public function getArtifacts() {
-    return $this->artifacts;
-  }
-
-  public function getKeys() {
-    return array_keys($this->artifacts);
-  }
-
-  public function length() {
-    return count($this->artifacts);
-  }
-
-  public function keyExists($key) {
-    return isset($this->artifacts[$key]);
-  }
-
-}
diff --git a/src/DrupalCI/Job/Results/JobResults.php b/src/DrupalCI/Job/Results/JobResults.php
deleted file mode 100644
index a95612f..0000000
--- a/src/DrupalCI/Job/Results/JobResults.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \DrupalCI\Job\Results\JobResults.
- */
-
-namespace DrupalCI\Job\Results;
-
-use DrupalCI\Console\Output;
-use DrupalCI\Plugin\JobTypes\JobInterface;
-
-class JobResults {
-
-  protected $current_stage;
-  public function getCurrentStage() {  return $this->current_stage;  }
-  public function setCurrentStage($stage) {  $this->current_stage = $stage;  }
-
-  protected $stage_results;
-  public function getStageResults() {  return $this->stage_results;  }
-  public function setStageResults(array $stage_results) {  $this->stage_results = $stage_results;  }
-  public function getResultByStage($stage) {  return $this->stage_results[$stage];  }
-  public function setResultByStage($stage, $result) {  $this->stage_results[$stage] = $result;  }
-
-  protected $current_step;
-  public function getCurrentStep() {  return $this->current_step;  }
-  public function setCurrentStep($step) {  $this->current_step = $step;  }
-
-  protected $step_results;
-  public function getStepResults() {  return $this->step_results;  }
-  public function setStepResults(array $step_results) {  $this->step_results = $step_results;  }
-  public function getResultByStep($stage, $step) {  return $this->step_results[$stage][$step];  }
-  public function setResultByStep($stage, $step, $result)  {  $this->step_results[$stage][$step] = $result;  }
-
-  protected $artifacts;
-  public function setArtifacts($artifacts) { $this->artifacts = $artifacts; }
-  public function getArtifacts() { return $this->artifacts; }
-
-  protected $publishers = [];
-  public function getPublishers() {  return $this->publishers;  }
-  public function setPublishers($publishers) {  $this->publishers = $publishers;  }
-  public function getPublisher($publisher) {  return $this->publishers[$publisher];  }
-
-
-  public function __construct(JobInterface $job) {
-    // Set up our initial $step_result values
-    $this->initStepResults($job);
-  }
-
-  protected function initStepResults(JobInterface $job) {
-    // Retrieve the build step tree from the job definition
-    $build_steps = $job->getJobDefinition()->getBuildSteps();
-    // Set up our initial $step_result values
-    $step_results = [];
-    foreach ($build_steps as $stage => $steps) {
-      foreach ($steps as $step => $value) {
-        $step_results[$stage][$step] = ['run status' => 'No run'];
-      }
-    }
-    $this->setStepResults($step_results);
-  }
-
-  public function updateStageStatus($build_stage, $status) {
-    $this->setCurrentStage($build_stage);
-    $this->setResultByStage($build_stage, $status);
-    // TODO: Determine if we have any publishers, and progress the build step if we do.
-    Output::writeln("<comment><options=bold>$status</options=bold> $build_stage</comment>");
-  }
-
-  public function updateStepStatus($build_stage, $build_step, $status) {
-    $this->setCurrentStep($build_step);
-    $this->setResultByStep($build_stage, $build_step, $status);
-    Output::writeln("<comment><options=bold>$status</options=bold> $build_stage:$build_step</comment>");
-  }
-
-
-  // TODO: Consider adding a 'job publisher' class for interim feedback and/or real-time display
-  /*
-    // Pasting this code here for future reference, once we revisit interacting with a results API.
-
-  public function prepServerForPublishing(JobDefinition $jobDefinition) {
-    // If we are publishing this job to a results server (or multiple), prep the server
-      $definition = $jobDefinition->getDefinition();
-      if (!empty($definition['publish']['drupalci_results'])) {
-      $results_data = $job_definition['publish']['drupalci_results'];
-      // $data format:
-      // i) array('config' => '<configuration filename>'),
-      // ii) array('host' => '...', 'username' => '...', 'password' => '...')
-      // or a mixed array of the above
-      // iii) array(array(...), array(...))
-      // Normalize data to the third format, if necessary
-      $results_data = (count($results_data) == count($results_data, COUNT_RECURSIVE)) ? [$results_data] : $results_data;
-    }
-    else {
-      $results_data = array();
-    }
-
-  public function publishProgressToServer() {
-    // If we are publishing this job to a results server (or multiple), update the progress on the server(s)
-    // TODO: Check current state, and don't progress if already there.
-    foreach ($results_data as $key => $instance) {
-      $job->configureResultsAPI($instance);
-      $api = $job->getResultsAPI();
-      $url = $api->getUrl();
-      // Retrieve the results node ID for the results server
-      $host = parse_url($url, PHP_URL_HOST);
-      $states = $api->states();
-      $results_id = $job->getResultsServerID();
-
-      foreach ($states as $subkey => $state) {
-        if ($build_step == $subkey) {
-          $api->progress($results_id[$host], $state['id']);
-          break;
-        }
-      }
-    }
-  }
-  */
-
-}
\ No newline at end of file
diff --git a/src/DrupalCI/Plugin/BuildSteps/configure/CompileDefinition.php b/src/DrupalCI/Plugin/BuildSteps/configure/CompileDefinition.php
new file mode 100644
index 0000000..d26f45c
--- /dev/null
+++ b/src/DrupalCI/Plugin/BuildSteps/configure/CompileDefinition.php
@@ -0,0 +1,185 @@
+<?php
+/**
+ * @file
+ * Contains \DrupalCI\Plugin\BuildSteps\configure\CompileDefinition
+ *
+ * Compiles a complete job definition from a hierarchy of sources.
+ * This hierarchy is defined as follows, which each level overriding the previous:
+ * 1. Out-of-the-box DrupalCI defaults
+ * 2. Local overrides defined in ~/.drupalci/config
+ * 3. 'DCI_' namespaced environment variable overrides
+ * 4. Test-specific overrides passed inside a DrupalCI test definition (e.g. .drupalci.yml)
+ * 5. Custom overrides located inside a test definition defined via the $source variable when calling this function.
+ */
+
+namespace DrupalCI\Plugin\BuildSteps\configure;
+
+use DrupalCI\Console\Output;
+use DrupalCI\Plugin\JobTypes\JobInterface;
+use DrupalCI\Plugin\PluginBase;
+use DrupalCI\Console\Helpers\ConfigHelper;
+use DrupalCI\Plugin\PluginManager;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * @PluginID("compile_definition")
+ */
+class CompileDefinition extends PluginBase {
+
+  /**
+   * @var \DrupalCI\Plugin\PluginManager;
+   */
+  protected $pluginManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function run(JobInterface $job, $data = NULL) {
+    Output::writeLn("<info>Calculating job definition</info>");
+    // Get and parse the default definition template (containing %DCI_*%
+    // placeholders) into the job definition.
+
+    // For 'generic' jobs, this is the file passed in on the
+    // 'drupalci run <filename>' command; and should be fully populated (though
+    // template placeholders *can* be supported).
+
+    // For other 'jobtype' jobs, this is the file located at
+    // DrupalCI/Plugin/JobTypes/<jobtype>/drupalci.yml.
+    if (!$definition = $this->loadYaml($job->getDefinitionFile())) {
+      $job->errorOutput('Error', 'Failed to load job definition YAML');
+    }
+    // Get and parse external (i.e. anything not from the default definition
+    // file) job argument parameters.  DrupalCI jobs are controlled via a
+    // hierarchy of configuration settings, which define the behaviour of the
+    // platform while running DrupalCI jobs.  This hierarchy is defined as
+    // follows, which each level overriding the previous:
+
+    // 1. Out-of-the-box DrupalCI platform defaults, as defined in DrupalCI/Plugin/JobTypes/JobBase->platformDefaults
+    $platform_defaults = $job->getPlatformDefaults();
+    if (!empty($platform_defaults)) {
+      Output::writeLn("<comment>Loading DrupalCI platform default arguments:</comment>");
+      Output::writeLn(implode(",", array_keys($platform_defaults)));
+    }
+
+    // 2. Out-of-the-box DrupalCI JobType defaults, as defined in DrupalCI/Plugin/JobTypes/<jobtype>->defaultArguments
+    $jobtype_defaults = $job->getDefaultArguments();
+    if (!empty($jobtype_defaults)) {
+      Output::writeLn("<comment>Loading job type default arguments:</comment>");
+      Output::writeLn(implode(",", array_keys($jobtype_defaults)));
+    }
+
+    // 3. Local overrides defined in ~/.drupalci/config
+    $confighelper = new ConfigHelper();
+    $local_overrides = $confighelper->getCurrentConfigSetParsed();
+    if (!empty($local_overrides)) {
+      Output::writeLn("<comment>Loading local DrupalCI environment config override arguments.</comment>");
+      Output::writeLn(implode(",", array_keys($local_overrides)));
+    }
+
+    // 4. 'DCI_' namespaced environment variable overrides
+    $environment_variables = $confighelper->getCurrentEnvVars();
+    if (!empty($environment_variables)) {
+      Output::writeLn("<comment>Loading local namespaced environment variable override arguments.</comment>");
+      Output::writeLn(implode(",", array_keys($environment_variables)));
+    }
+
+    // 5. Additional variables passed in via the command line
+    // TODO: Not yet implemented
+    $cli_variables = ['DCI_JobBuildId' => $job->getBuildId()];
+
+    // Combine the above to generate the final array of DCI_* key=>value pairs
+    $dci_variables = $cli_variables + $environment_variables + $local_overrides + $jobtype_defaults + $platform_defaults;
+
+    // Reorder array, placing priority variables at the front
+    if (!empty($job->priorityArguments)) {
+      $original_array = $dci_variables;
+      $original_keys = array_keys($original_array);
+      $ordered_variables = [];
+      foreach ($job->priorityArguments as $element) {
+        if (in_array($element, $original_keys)) {
+          $ordered_variables[$element] = $original_array[$element];
+          unset($original_array[$element]);
+        }
+      }
+      $dci_variables = array_merge($ordered_variables, $original_array);
+    }
+
+    // For each DCI_* element in the array, check to see if a variable
+    // preprocessor exists, and process it if it does.
+    $replacements = [];
+    $plugin_manager = $this->getPreprocessPluginManager();
+    foreach ($dci_variables as $key => &$value) {
+      if (preg_match('/^DCI_(.+)$/i', $key, $matches)) {
+        $name = strtolower($matches[1]);
+        if ($plugin_manager->hasPlugin('variable', $name)) {
+          /** @var \DrupalCI\Plugin\Preprocess\VariableInterface $plugin */
+          $plugin = $plugin_manager->getPlugin('variable', $name);
+          // @TODO: perhaps this should be on the annotation.
+          $new_keys = $plugin->target();
+          if (!is_array($new_keys)) {
+            $new_keys = [$new_keys];
+          }
+          // @TODO: error handling.
+          foreach ($new_keys as $new_key) {
+            // Only process variable plugins if the variable being changed actually exists.
+            if (!empty($dci_variables[$new_key])) {
+              $dci_variables[$new_key] = $plugin->process($dci_variables[$new_key], $value, $new_key);
+            }
+          }
+        }
+      }
+    }
+    // Foreach DCI_* pair in the array, check if a definition plugin exists,
+    // and process if it does.  We pass in the test definition template and
+    // complete array of DCI_* variables.
+    foreach ($dci_variables as $key => $value) {
+      if (preg_match('/^DCI_(.+)$/', $key, $matches)) {
+        $name = strtolower($matches[1]);
+        $replacements["%$key%"] = $value;
+        if ($plugin_manager->hasPlugin('definition', $name)) {
+          $plugin_manager->getPlugin('definition', $name)
+            ->process($definition, $value, $dci_variables);
+        }
+      }
+    }
+
+    // Add support for substituting '%HOME%' with the $HOME env variable
+    $replacements["%HOME%"] = getenv("HOME");
+
+    // Process DCI_* variable substitution into test definition template
+    $search = array_keys($replacements);
+    $replace = array_values($replacements);
+    array_walk_recursive($definition, function (&$value) use ($search, $replace) {
+      $value = str_ireplace($search, $replace, $value);
+    });
+    // Attach the complete set of build variables and processed job definition to the job object
+    $job->setBuildVars($dci_variables + $job->getBuildVars());
+    $job->setDefinition($definition);
+    return;
+  }
+
+  /**
+   * Given a file, returns an array containing the parsed YAML contents from that file
+   *
+   * @param $source
+   *   A YAML source file
+   * @return array
+   *   an array containing the parsed YAML contents from the source file
+   */
+  protected function loadYaml($source) {
+    if ($content = file_get_contents($source)) {
+      return Yaml::parse($content);
+    }
+    return [];
+  }
+
+  /**
+   * @return \DrupalCI\Plugin\PluginManager
+   */
+  protected function getPreprocessPluginManager() {
+    if (!isset($this->pluginManager)) {
+      $this->pluginManager = new PluginManager('Preprocess');
+    }
+    return $this->pluginManager;
+  }
+}
diff --git a/src/DrupalCI/Plugin/BuildSteps/configure/PrepResults.php b/src/DrupalCI/Plugin/BuildSteps/configure/PrepResults.php
index 58b32cf..5e8265c 100644
--- a/src/DrupalCI/Plugin/BuildSteps/configure/PrepResults.php
+++ b/src/DrupalCI/Plugin/BuildSteps/configure/PrepResults.php
@@ -22,7 +22,7 @@ class PrepResults extends PluginBase {
    */
   public function run(JobInterface $job, $data = NULL) {
     // Retrieve job definition array
-    $definition = $job->getJobDefinition->getDefinition();
+    $definition = $job->getDefinition();
     // We only need to prep the results site if there is a publish['drupalci_results'] build step.
     if (empty($definition['publish']['drupalci_results'])) {
       return;
@@ -30,10 +30,10 @@ class PrepResults extends PluginBase {
 
     // The results node could be defined further upstream, and passed in to us
     // in an environment variable (DCI_JobID);
-    $upstream_id = $job->getBuildVar('DCI_JobID');
+    $upstream_id = $job->getBuildvar('DCI_JobID');
     // If we have a job ID, we also need a results server URL or config file location.
-    $results_server = $job->getBuildVar('DCI_ResultsServer');
-    $results_server_config = $job->getBuildVar('DCI_ResultsServerConfig');
+    $results_server = $job->getBuildvar('DCI_ResultsServer');
+    $results_server_config = $job->getBuildvar('DCI_ResultsServerConfig');
     if (!empty($upstream_id)) {
       // Job ID found.  Results node is assumed to already have been created.
       if (!empty($results_server_config)) {
diff --git a/src/DrupalCI/Plugin/BuildSteps/configure/SetupDirectories.php b/src/DrupalCI/Plugin/BuildSteps/configure/SetupDirectories.php
new file mode 100644
index 0000000..f25b21b
--- /dev/null
+++ b/src/DrupalCI/Plugin/BuildSteps/configure/SetupDirectories.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * @file
+ * Contains \DrupalCI\Plugin\BuildSteps\configure\SetupDirectories
+ *
+ * Prepares the initial local working directory prior to checkout/copying code
+ */
+namespace DrupalCI\Plugin\BuildSteps\configure;
+
+use DrupalCI\Console\Output;
+use DrupalCI\Plugin\JobTypes\JobInterface;
+
+/**
+ * @PluginID("setup_directories")
+ */
+class SetupDirectories {
+  /**
+   * {@inheritdoc}
+   */
+  public function run(JobInterface $job, $data = NULL) {
+    Output::writeLn("<info>Creating codebase data volume directory</info>");
+    // Setup codebase and working directories
+    // $this->setupCodebase($job);
+    $this->setupWorkingDir($job);
+  }
+/*
+  // TODO: Setting CodeBase results in $job->definition file being empty.  Look into CompileDefinition->getDefinitionFile()
+  public function setupCodebase(JobInterface $job) {
+    $arguments = $job->getBuildVars();
+    // Check if the source codebase directory has been specified
+    if (empty($arguments['DCI_CodeBase'])) {
+      // If no explicit codebase provided, assume we are using the code in the local directory.
+      $arguments['DCI_CodeBase'] = "./";
+      $job->setBuildVars($arguments);
+    }
+    else {
+      Output::writeLn("<comment>Using codebase directory defined in DCI_CodeBase: <options=bold>${arguments['DCI_CodeBase']}</options=bold></comment>");
+    }
+  }
+*/
+  public function setupWorkingDir(JobInterface $job) {
+    $arguments = $job->getBuildVars();
+    // Check if the target working directory has been specified.
+    if (empty($arguments['DCI_CheckoutDir'])) {
+      // Case:  No explicit working directory defined.
+      // Generate a working directory in the system temporary directory.
+      $build_id = $job->getBuildId();
+      $tmpdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $build_id;
+      if (!is_dir($tmpdir)) {
+        $result = mkdir($tmpdir, 0777, TRUE);
+        // $tmpdir = $this->create_tempdir($job, sys_get_temp_dir() . '/drupalci/', $job->jobType . "-");
+        if (!$result) {
+          // Error creating checkout directory
+          $job->errorOutput("Error", "Failure encountered while attempting to create a local checkout directory");
+          return FALSE;
+        }
+      }
+      Output::writeLn("<comment>Checkout directory created at <info>$tmpdir</info></comment>");
+      $job->setBuildVar('DCI_CheckoutDir', $tmpdir);
+      $arguments['DCI_CheckoutDir'] = $tmpdir;
+    }
+    elseif ($arguments['DCI_CheckoutDir'] != $arguments['DCI_CodeBase']) {
+      // Case: Working directory defined, and differs from the CodeBase directory.
+      // TODO: Resolve / Sync up use of DCI_CodeBase and DCI_UseLocalCodebase
+      // Create checkout directory
+      $result = $this->create_local_checkout_dir($job);
+      // create_local_checkout_dir will ensure the checkout directory is
+      // within the system temporary directory, to ensure that we don't provide
+      // access to the entire file system.
+
+      // Pass through any errors encountered while creating the directory
+      // TODO: This appears to be redundant ... verify and remove.
+      if ($result == -1) {
+        return -1;
+      }
+    }
+    // Update the checkout directory in the class object
+    $job->setWorkingDir($arguments['DCI_CheckoutDir']);
+  }
+/*
+  protected function create_tempdir(JobInterface $job, $dir=NULL,$prefix=NULL) {
+    // PHP seems to have trouble creating temporary unique directories with the appropriate permissions,
+    // So we create a temp file to get the unique filename, then mkdir a directory in it's place.
+    $prefix = empty($prefix) ? "drupalci-" : $prefix;
+    $tmpdir = ($dir && is_dir($dir)) ? $dir : sys_get_temp_dir();
+    $tempname = tempnam($tmpdir, $prefix);
+    if (empty($tempname)) {
+      // Unable to create temp filename
+      $job->errorOutput("Error", "Unable to create temporary directory inside of $tmpdir.");
+      return;
+    }
+    $tempdir = $tempname;
+    unlink($tempname);
+    if (mkdir($tempdir)) {
+      return $tempdir;
+    }
+    else {
+      // Unable to create temp directory
+      $job->errorOutput("Error", "Error encountered while attempting to create temporary directory $tempdir.");
+      return;
+    }
+  }
+*/
+  protected function create_local_checkout_dir(JobInterface $job) {
+    $arguments = $job->getBuildVars();
+    $directory = $arguments['DCI_CheckoutDir'];
+    $tempdir = sys_get_temp_dir();
+
+    // Prefix the system temp dir on the DCI_CheckoutDir variable if needed
+    if (strpos($directory, $tempdir) !== 0) {
+      // If not, prefix the system temp directory on the variable.
+      if ($directory[0] != "/") {
+        $directory = "/" . $directory;
+      }
+      $arguments['DCI_CheckoutDir'] = $tempdir . $directory;
+      $job->setBuildVars($arguments);
+    }
+
+    // Check if the DCI_CheckoutDir exists within the /tmp directory, or create it if not
+    $path = realpath($arguments['DCI_CheckoutDir']);
+    if ($path !== FALSE) {
+      // Directory exists.  Check that we're still in /tmp
+      if (!$this->validate_checkout_dir($job)) {
+        // Something bad happened.  Attempt to transverse out of the /tmp dir, perhaps?
+        $job->errorOutput("Error", "Detected an invalid local checkout directory.  The checkout directory must reside somewhere within the system temporary file directory.");
+        return;
+      }
+      else {
+        // Directory is within the system temp dir.
+        Output::writeLn("<comment>Found existing local checkout directory <info>$path</info></comment>");
+        return;
+      }
+    }
+    elseif ($path === FALSE) {
+      // Directory doesn't exist, so create it.
+      $directory = $arguments['DCI_CheckoutDir'];
+      mkdir($directory, 0777, true);
+      Output::writeLn("<comment>Checkout Directory created at <info>$directory</info>");
+      // Ensure we are under the system temp dir
+      if (!$this->validate_checkout_dir($job)) {
+        // Something bad happened.  Attempt to transverse out of the /tmp dir, perhaps?
+        $job->errorOutput("Error", "DCI_CheckoutDir must reside somewhere within the system temporary file directory. You may wish to manually remove the directory created above.");
+        return;
+      }
+    }
+  }
+
+  public function validate_checkout_dir(JobInterface $job) {
+    $arguments = $job->getBuildVars();
+    $path = realpath($arguments['DCI_CheckoutDir']);
+    $tmpdir = sys_get_temp_dir();
+    if (strpos($path, $tmpdir) === 0) {
+      return TRUE;
+    }
+    return FALSE;
+  }
+
+}
diff --git a/src/DrupalCI/Plugin/BuildSteps/configure/ValidateDefinition.php b/src/DrupalCI/Plugin/BuildSteps/configure/ValidateDefinition.php
new file mode 100644
index 0000000..fbfd6d6
--- /dev/null
+++ b/src/DrupalCI/Plugin/BuildSteps/configure/ValidateDefinition.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @file
+ * Contains \DrupalCI\Plugin\BuildSteps\configure\ValidateDefinition
+ *
+ * Validates a compiled job definition against the job type definition:
+ * 1. Verifies all 'mandatory' parameters present
+ * 2. (TODO) Strips out any parameters not specified in the 'allowed' list for that job type
+ */
+
+namespace DrupalCI\Plugin\BuildSteps\configure;
+
+use DrupalCI\Plugin\JobTypes\JobInterface;
+use DrupalCI\Plugin\PluginBase;
+
+/**
+ * @PluginID("validate_definition")
+ */
+class ValidateDefinition extends PluginBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function run(JobInterface $job, $data = NULL) {
+    // TODO: Ensure that all 'required' arguments are defined
+    $definition = $job->getDefinition();
+    $failflag = FALSE;
+    foreach ($job->getRequiredArguments() as $env_var => $yaml_loc) {
+      if (!empty($job->getBuildVars()[$env_var])) {
+        continue;
+      }
+      else {
+        // Look for the appropriate array structure in the job definition file
+        // eg: environment:db
+        $keys = explode(":", $yaml_loc);
+        $eval = $definition;
+        foreach ($keys as $key) {
+          if (!empty($eval[$key])) {
+            // Check if the next level contains a numeric [0] key, indicating a
+            // nested array of parameters.  If found, skip this level of the
+            // array.
+            if (isset($eval[$key][0])) {
+              $eval = $eval[$key][0];
+            }
+            else {
+              $eval=$eval[$key];
+            }
+          }
+          else {
+            // Missing a required key in the array key chain
+            $failflag = TRUE;
+            break;
+          }
+        }
+        if (!$failflag) {
+          continue;
+        }
+      }
+      // If processing gets to here, we're missing a required variable
+      $job->errorOutput("Failed", "Required test parameter <options=bold>'$env_var'</options=bold> not found in environment variables, and <options=bold>'$yaml_loc'</options=bold> not found in job definition file.");
+      // TODO: Graceful handling of failed exit states
+      return FALSE;
+    }
+    // TODO: Strip out arguments which are not defined in the 'Available' arguments array
+    return TRUE;
+  }
+}
\ No newline at end of file
diff --git a/src/DrupalCI/Plugin/BuildSteps/dbcreate/MariaDB.php b/src/DrupalCI/Plugin/BuildSteps/dbcreate/MariaDB.php
index 9f35890..7e97cb6 100644
--- a/src/DrupalCI/Plugin/BuildSteps/dbcreate/MariaDB.php
+++ b/src/DrupalCI/Plugin/BuildSteps/dbcreate/MariaDB.php
@@ -18,7 +18,7 @@ class MariaDB extends ContainerCommand {
    * {@inheritdoc}
    */
   public function run(JobInterface $job, $data) {
-    $parts = parse_url($job->getBuildVar('DCI_DBUrl'));
+    $parts = parse_url($job->getBuildvar('DCI_DBUrl'));
     $parts['scheme'] = 'mysql';
     $host = $parts['host'];
     $user = $parts['user'];
diff --git a/src/DrupalCI/Plugin/BuildSteps/dbcreate/MySQL.php b/src/DrupalCI/Plugin/BuildSteps/dbcreate/MySQL.php
index ad126f3..e847774 100644
--- a/src/DrupalCI/Plugin/BuildSteps/dbcreate/MySQL.php
+++ b/src/DrupalCI/Plugin/BuildSteps/dbcreate/MySQL.php
@@ -18,7 +18,7 @@ class MySQL extends ContainerCommand {
    * {@inheritdoc}
    */
   public function run(JobInterface $job, $data) {
-    $parts = parse_url($job->getBuildVar('DCI_DBUrl'));
+    $parts = parse_url($job->getBuildvar('DCI_DBUrl'));
     $host = $parts['host'];
     $user = $parts['user'];
     $pass = $parts['pass'];
diff --git a/src/DrupalCI/Plugin/BuildSteps/dbcreate/PostgreSQL.php b/src/DrupalCI/Plugin/BuildSteps/dbcreate/PostgreSQL.php
index 6de4252..6d2e6d1 100644
--- a/src/DrupalCI/Plugin/BuildSteps/dbcreate/PostgreSQL.php
+++ b/src/DrupalCI/Plugin/BuildSteps/dbcreate/PostgreSQL.php
@@ -19,7 +19,7 @@ class PostgreSQL extends ContainerCommand {
    */
   public function run(JobInterface $job, $data) {
 
-    $parts = parse_url($job->getBuildVar('DCI_DBUrl'));
+    $parts = parse_url($job->getBuildvar('DCI_DBUrl'));
     $host = $parts['host'];
     $user = $parts['user'];
     $pass = $parts['pass'];
diff --git a/src/DrupalCI/Plugin/BuildSteps/environment/DbEnvironment.php b/src/DrupalCI/Plugin/BuildSteps/environment/DbEnvironment.php
index ca0ec9d..0acb164 100644
--- a/src/DrupalCI/Plugin/BuildSteps/environment/DbEnvironment.php
+++ b/src/DrupalCI/Plugin/BuildSteps/environment/DbEnvironment.php
@@ -23,7 +23,7 @@ class DbEnvironment extends EnvironmentBase {
    */
   public function run(JobInterface $job, $data) {
     // We don't need to initialize any service container for SQLite.
-    if (strpos($job->getBuildVar('DCI_DBVersion'), 'sqlite') === 0) {
+    if (strpos($job->getBuildvar('DCI_DBVersion'), 'sqlite') === 0) {
       return;
     }
 
@@ -33,7 +33,8 @@ class DbEnvironment extends EnvironmentBase {
     $data = is_array($data) ? $data : [$data];
     Output::writeLn("<info>Parsing required database container image names ...</info>");
     $containers = $this->buildImageNames($data, $job);
-    if ($valid = $this->validateImageNames($containers, $job)) {
+    $valid = $this->validateImageNames($containers, $job);
+    if (!empty($valid)) {
       $service_containers = $job->getServiceContainers();
       $service_containers['db'] = $containers;
       $job->setServiceContainers($service_containers);
diff --git a/src/DrupalCI/Plugin/BuildSteps/environment/EnvironmentBase.php b/src/DrupalCI/Plugin/BuildSteps/environment/EnvironmentBase.php
index 1e5756f..52f09fc 100644
--- a/src/DrupalCI/Plugin/BuildSteps/environment/EnvironmentBase.php
+++ b/src/DrupalCI/Plugin/BuildSteps/environment/EnvironmentBase.php
@@ -27,8 +27,8 @@ abstract class EnvironmentBase extends PluginBase {
         $image = $manager->find($name);
       }
       catch (ImageNotFoundException $e) {
-        Output::error("Missing Image", "Required container image <options=bold>'$name'</options=bold> not found.");
-        $job->error();
+        $job->errorOutput("Failed", "Required container image <options=bold>'$name'</options=bold> not found.");
+        // TODO: Robust error handling.
         return FALSE;
       }
       $id = substr($image->getID(), 0, 8);
diff --git a/src/DrupalCI/Plugin/BuildSteps/publish/GatherArtifacts.php b/src/DrupalCI/Plugin/BuildSteps/publish/GatherArtifacts.php
index b03e0ff..046742c 100644
--- a/src/DrupalCI/Plugin/BuildSteps/publish/GatherArtifacts.php
+++ b/src/DrupalCI/Plugin/BuildSteps/publish/GatherArtifacts.php
@@ -46,7 +46,7 @@ class GatherArtifacts extends PluginBase {
         $destination_filename = $artifact->getValue();
 
         // Retrieve the job definition from the job
-        $definition = $job->getJobDefinition()->getDefinition();
+        $definition = $job->getDefinition();
         // write the job definition out to a file in the artifact directory on the container.
         if (!empty($destination_filename)) {
           $file = $target_directory . DIRECTORY_SEPARATOR . $destination_filename;
diff --git a/src/DrupalCI/Plugin/BuildSteps/publish/JunitXMLFormat.php b/src/DrupalCI/Plugin/BuildSteps/publish/JunitXMLFormat.php
index 1e4e5ec..c508c00 100644
--- a/src/DrupalCI/Plugin/BuildSteps/publish/JunitXMLFormat.php
+++ b/src/DrupalCI/Plugin/BuildSteps/publish/JunitXMLFormat.php
@@ -49,7 +49,7 @@ class JunitXMLFormat extends PluginBase {
     // Load the list of tests from the testgroups.txt build artifact
     // Assumes that gatherArtifacts plugin has run.
     // TODO: Verify that gatherArtifacts has ran.
-    $source_dir = $job->getJobCodebase()->getWorkingDir();
+    $source_dir = $job->getBuildVar('DCI_CheckoutDir');
     // TODO: Temporary hack.  Strip /checkout off the directory
     $artifact_dir = preg_replace('#/checkout$#', '', $source_dir);
     $this->loadTestList($source_dir . DIRECTORY_SEPARATOR . 'artifacts/testgroups.txt');
diff --git a/src/DrupalCI/Plugin/BuildSteps/setup/Checkout.php b/src/DrupalCI/Plugin/BuildSteps/setup/Checkout.php
index f712d45..57895cb 100644
--- a/src/DrupalCI/Plugin/BuildSteps/setup/Checkout.php
+++ b/src/DrupalCI/Plugin/BuildSteps/setup/Checkout.php
@@ -43,8 +43,7 @@ class Checkout extends SetupBase {
           $this->setupCheckoutGit($job, $details);
           break;
       }
-      // Break out of loop if we've encountered any errors
-      if ($job->getErrorState() !== FALSE) {
+      if ($job->getErrorState()) {
         break;
       }
     }
@@ -53,19 +52,17 @@ class Checkout extends SetupBase {
 
   protected function setupCheckoutLocal(JobInterface $job, $details) {
     $source_dir = isset($details['source_dir']) ? $details['source_dir'] : './';
-    $checkout_dir = isset($details['checkout_dir']) ? $details['checkout_dir'] : $job->getJobCodebase()->getWorkingDir();
+    $checkout_dir = isset($details['checkout_dir']) ? $details['checkout_dir'] : $job->getWorkingDir();
     // TODO: Ensure we don't end up with double slashes
     // Validate source directory
     if (!is_dir($source_dir)) {
-      Output::error("Directory error", "The source directory <info>$source_dir</info> does not exist.");
-      $job->error();
+      $job->errorOutput("Error", "The source directory <info>$source_dir</info> does not exist.");
       return;
     }
     // Validate target directory.  Must be within workingdir.
     if (!($directory = $this->validateDirectory($job, $checkout_dir))) {
       // Invalidate checkout directory
-      Output::error("Directory error", "The checkout directory <info>$directory</info> is invalid.");
-      $job->error();
+      $job->errorOutput("Error", "The checkout directory <info>$directory</info> is invalid.");
       return;
     }
     Output::writeln("<comment>Copying files from <options=bold>$source_dir</options=bold> to the local checkout directory <options=bold>$directory</options=bold> ... </comment>");
@@ -74,8 +71,7 @@ class Checkout extends SetupBase {
     $exclude_var = isset($details['DCI_EXCLUDE']) ? "" : "--exclude=\".git\"";
     $this->exec("rsync -a $exclude_var  $source_dir/. $directory", $cmdoutput, $result);
     if ($result !== 0) {
-      Output::error("Copy error", "Error encountered while attempting to copy code to the local checkout directory.");
-      $job->error();
+      $job->errorOutput("Failed", "Error encountered while attempting to copy code to the local checkout directory.");
       return;
     }
     Output::writeLn("<comment>DONE</comment>");
@@ -85,13 +81,12 @@ class Checkout extends SetupBase {
     Output::writeLn("<info>Entering setup_checkout_git().</info>");
     $repo = isset($details['repo']) ? $details['repo'] : 'git://drupalcode.org/project/drupal.git';
     $git_branch = isset($details['branch']) ? $details['branch'] : 'master';
-    $checkout_directory = isset($details['checkout_dir']) ? $details['checkout_dir'] : $job->getJobCodebase()->getWorkingDir();
+    $checkout_directory = isset($details['checkout_dir']) ? $details['checkout_dir'] : $job->getWorkingDir();
     // TODO: Ensure we don't end up with double slashes
     // Validate target directory.  Must be within workingdir.
     if (!($directory = $this->validateDirectory($job, $checkout_directory))) {
       // Invalid checkout directory
-      Output::error("Directory Error", "The checkout directory <info>$directory</info> is invalid.");
-      $job->error();
+      $job->errorOutput("Error", "The checkout directory <info>$directory</info> is invalid.");
       return;
     }
     Output::writeLn("<comment>Performing git checkout of $repo $git_branch branch to $directory.</comment>");
@@ -105,8 +100,8 @@ class Checkout extends SetupBase {
     $this->exec($cmd, $cmdoutput, $result);
     if ($result !==0) {
       // Git threw an error.
-      Output::error("Checkout Error", "The git checkout returned an error.  Error Code: $result");
-      $job->error();
+      $job->errorOutput("Checkout failed", "The git checkout returned an error.");
+      // TODO: Pass on the actual return value for the git checkout
       return;
     }
 
diff --git a/src/DrupalCI/Plugin/BuildSteps/setup/Fetch.php b/src/DrupalCI/Plugin/BuildSteps/setup/Fetch.php
index 2882f0c..de4cb25 100644
--- a/src/DrupalCI/Plugin/BuildSteps/setup/Fetch.php
+++ b/src/DrupalCI/Plugin/BuildSteps/setup/Fetch.php
@@ -37,17 +37,15 @@ class Fetch extends SetupBase {
       // URL and target directory
       // TODO: Ensure $details contains all required parameters
       if (empty($details['url'])) {
-        Output::error("Fetch error", "No valid target file provided for fetch command.");
-        $job->error();
+        $job->errorOutput("Error", "No valid target file provided for fetch command.");
         return;
       }
       $url = $details['url'];
-      $workingdir = $job->getJobCodebase()->getWorkingDir();
+      $workingdir = $job->getWorkingDir();
       $fetchdir = (!empty($details['fetch_directory'])) ? $details['fetch_directory'] : $workingdir;
       if (!($directory = $this->validateDirectory($job, $fetchdir))) {
         // Invalid checkout directory
-        Output:error("Fetch error", "The fetch directory <info>$directory</info> is invalid.");
-        $job->error();
+        $job->errorOutput("Error", "The fetch directory <info>$directory</info> is invalid.");
         return;
       }
       $info = pathinfo($url);
@@ -59,8 +57,7 @@ class Fetch extends SetupBase {
           ->send();
       }
       catch (\Exception $e) {
-        Output::error("Write error", "An error was encountered while attempting to write <info>$url</info> to <info>$directory</info>");
-        $job->error();
+        $job->errorOutput("Error", "An error was encountered while attempting to write <info>$url</info> to <info>$directory</info>");
         return;
       }
       Output::writeLn("<comment>Fetch of <options=bold>$url</options=bold> to <options=bold>$destination_file</options=bold> complete.</comment>");
diff --git a/src/DrupalCI/Plugin/BuildSteps/setup/Patch.php b/src/DrupalCI/Plugin/BuildSteps/setup/Patch.php
index 9e8e019..12815c1 100644
--- a/src/DrupalCI/Plugin/BuildSteps/setup/Patch.php
+++ b/src/DrupalCI/Plugin/BuildSteps/setup/Patch.php
@@ -29,18 +29,16 @@ class Patch extends SetupBase {
     Output::writeLn("<info>Entering setup_patch().</info>");
     foreach ($data as $key => $details) {
       if (empty($details['patch_file'])) {
-        Output::error("Patch error", "No valid patch file provided for the patch command.");
-        $job->error();
+        $job->errorOutput("Error", "No valid patch file provided for the patch command.");
         return;
       }
-      $workingdir = realpath($job->getJobCodebase()->getWorkingDir());
+      $workingdir = realpath($job->getWorkingDir());
       $patchfile = $details['patch_file'];
       $patchdir = (!empty($details['patch_dir'])) ? $details['patch_dir'] : $workingdir;
       // Validate target directory.
       if (!($directory = $this->validateDirectory($job, $patchdir))) {
         // Invalid checkout directory
-        Output::error("Patch Error", "The patch directory <info>$directory</info> is invalid.");
-        $job->error();
+        $job->errorOutput("Error", "The patch directory <info>$directory</info> is invalid.");
         return;
       }
       $cmd = "cd $directory && git apply -v -p1 $patchfile && cd -";
@@ -48,9 +46,8 @@ class Patch extends SetupBase {
       $this->exec($cmd, $cmdoutput, $result);
       if ($result !== 0) {
         // The command threw an error.
+        $job->errorOutput("Patch failed", "The patch attempt returned an error.");
         Output::writeLn($cmdoutput);
-        Output::error("Patch Error", "The patch attempt returned an error.  Error code: $result");
-        $job->error();
         // TODO: Pass on the actual return value for the patch attempt
         return;
       }
diff --git a/src/DrupalCI/Plugin/BuildSteps/setup/SetupBase.php b/src/DrupalCI/Plugin/BuildSteps/setup/SetupBase.php
index 2c196ef..15ba263 100644
--- a/src/DrupalCI/Plugin/BuildSteps/setup/SetupBase.php
+++ b/src/DrupalCI/Plugin/BuildSteps/setup/SetupBase.php
@@ -15,7 +15,7 @@ abstract class SetupBase extends PluginBase {
 
   protected function validateDirectory(JobInterface $job, $dir) {
     // Validate target directory.  Must be within workingdir.
-    $working_dir = $job->getJobCodebase()->getWorkingDir();
+    $working_dir = $job->getWorkingDir();
     $true_dir = realpath($dir);
     if (!empty($true_dir)) {
       if ($true_dir == realpath($working_dir)) {
@@ -43,8 +43,7 @@ abstract class SetupBase extends PluginBase {
     // Validate that resulting directory is still within the working directory path.
     if (!strpos(realpath($directory), realpath($working_dir)) === 0) {
       // Invalid checkout directory
-      Output::error("Directory error", "The checkout directory <info>$directory</info> is invalid.");
-      $job->error();
+      $job->errorOutput("Error", "The checkout directory <info>$directory</info> is invalid.");
       return FALSE;
     }
 
diff --git a/src/DrupalCI/Plugin/JobTypes/JobBase.php b/src/DrupalCI/Plugin/JobTypes/JobBase.php
index 0e24bef..22c2eec 100644
--- a/src/DrupalCI/Plugin/JobTypes/JobBase.php
+++ b/src/DrupalCI/Plugin/JobTypes/JobBase.php
@@ -9,11 +9,8 @@ namespace DrupalCI\Plugin\JobTypes;
 use Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use DrupalCI\Console\Output;
-use DrupalCI\Job\Results\Artifacts\BuildArtifact;
-use DrupalCI\Job\Results\Artifacts\BuildArtifactList;
-use DrupalCI\Job\CodeBase\JobCodeBase;
-use DrupalCI\Job\Definition\JobDefinition;
-use DrupalCI\Job\Results\JobResults;
+use DrupalCI\Job\Artifacts\BuildArtifact;
+use DrupalCI\Job\Artifacts\BuildArtifactList;
 use DrupalCIResultsApi\Api;
 use Symfony\Component\Console\Output\OutputInterface;
 use Symfony\Component\Console\Tests\Output\ConsoleOutputTest;
@@ -26,133 +23,61 @@ use Docker\Container;
 
 class JobBase extends ContainerBase implements JobInterface {
 
-  /**
-   * Stores the job type
-   *
-   * @var string
-   */
-  protected $jobType = 'base';
-  public function getJobType() {  return $this->jobType;  }
+  // Defines the job type
+  public $jobtype = 'base';
 
-  /**
-   * Stores the calling command's output buffer
-   *
-   * @var \Symfony\Component\Console\Output\OutputInterface
-   */
-  public $output;
-  public function setOutput(OutputInterface $output) {  $this->output = $output;  }
-  public function getOutput() {  return $this->output;  }
+  // Defines a unique build ID
+  public $buildId;
 
   /**
-   * Stores a build ID for this job
-   *
-   * @var string
+   * @param mixed $buildId
    */
-  protected $buildId;
-  public function getBuildId() {  return $this->buildId;  }
-  public function setBuildId($buildId) {  $this->buildId = $buildId;  }
-
-  /**
-   * Stores the job definition object for this job
-   *
-   * @var \DrupalCI\Job\Definition\JobDefinition
-   */
-  protected $jobDefinition = NULL;
-  public function getJobDefinition() {  return $this->jobDefinition;  }
-  public function setJobDefinition(JobDefinition $job_definition) {  $this->jobDefinition = $job_definition; }
-
-  /**
-   * Stores the codebase object for this job
-   *
-   * @var \DrupalCI\Job\CodeBase\JobCodebase
-   */
-  protected $jobCodebase;
-  public function getJobCodebase() {  return $this->jobCodebase;  }
-  public function setJobCodebase(JobCodeBase $job_codebase)  {  $this->jobCodebase = $job_codebase;  }
-
-  /**
-   * Stores the results object for this job
-   *
-   * @var \DrupalCI\Job\Results\JobResults
-   */
-  protected $jobResults;
-  public function getJobResults() {  return $this->jobResults;  }
-  public function setJobResults(JobResults $job_results)  {  $this->jobResults = $job_results;  }
+  public function setBuildId($buildId)
+  {
+    $this->buildId = $buildId;
+  }
 
   /**
-   * Defines argument variable names which are valid for this job type
-   *
-   * @var array
+   * @return mixed
    */
-  protected $availableArguments = array();
-  public function getAvailableArguments() {  return $this->availableArguments;  }
+  public function getBuildId()
+  {
+    return $this->buildId;
+  }
 
-  /**
-   * Defines the default arguments which are valid for this job type
-   *
-   * @var array
-   */
-  protected $defaultArguments = array();
-  public function getDefaultArguments() {  return $this->defaultArguments;  }
+  // Defines the job definition file
+  protected $jobDefinitionFile;
 
-  /**
-   * Defines the required arguments which are necessary for this job type
-   *
-   * Format:  array('ENV_VARIABLE_NAME' => 'CONFIG_FILE_LOCATION'), where
-   * CONFIG_FILE_LOCATION is a colon-separated nested location for the
-   * equivalent variable in a job definition file.
-   *
-   * @var array
-   */
-  protected $requiredArguments = array();   // eg:   'DCI_DBVersion' => 'environment:db'
-  public function getRequiredArguments() {  return $this->requiredArguments;  }
+  // Defines argument variable names which are valid for this job type
+  public $availableArguments = array();
 
-  /**
-   * Defines initial platform defaults for all jobs (if not overridden).
-   *
-   * @var array
-   */
-  protected $platformDefaults = array(
-    "DCI_CoreProject" => "Drupal",
-    // DCI_WorkingDir defaults to a 'jobtype-buildID' directory in the system temp directory.
+  // Defines platform defaults which apply for all jobs.  (Can still be overridden by per-job defaults)
+  public $platformDefaults = array(
+    "DCI_CodeBase" => "./",
+    "DCI_IsDrupal" => true,
+    "DCI_DrupalVersion" => "8.0.x",
+    // DCI_CheckoutDir defaults to a random directory in the system temp directory.
   );
-  public function getPlatformDefaults() {  return $this->platformDefaults;  }
-
-  /**
-   * Stores build variables which need to be persisted between build steps
-   *
-   * @var array
-   */
-  protected $buildVars = array();
-  public function getBuildVars() {  return $this->buildVars;  }
-  public function setBuildVars(array $build_vars) {  $this->buildVars = $build_vars;  }
-  public function getBuildVar($build_var) {  return isset($this->buildVars[$build_var]) ? $this->buildVars[$build_var] : NULL;  }
-  public function setBuildVar($build_var, $value) {  $this->buildVars[$build_var] = $value;  }
-
-  /**
-   * Stores our Docker Container manager
-   *
-   * @var \Docker\Docker
-   */
-  protected $docker;
-
-  /**
-   * @return \Docker\Docker
-   */
-  public function getDocker()
-  {
-    $client = Client::createWithEnv();
-    if (null === $this->docker) {
-      $this->docker = new Docker($client);
-    }
-    return $this->docker;
-  }
-
 
+  // Defines the default arguments which are valid for this job type
+  public $defaultArguments = array();
 
+  // Defines the required arguments which are necessary for this job type
+  // Format:  array('ENV_VARIABLE_NAME' => 'CONFIG_FILE_LOCATION'), where
+  // CONFIG_FILE_LOCATION is a colon-separated nested location for the
+  // equivalent var in a job definition file.
+  public $requiredArguments = array(
+    // eg:   'DCI_DBVersion' => 'environment:db'
+  );
 
+  // Placeholder which holds the parsed job definition file for this job
+  public $jobDefinition = NULL;
 
+  // Error status
+  public $errorStatus = 0;
 
+  // Default working directory
+  public $workingDirectory = "./";
 
   /**
    * @var array
@@ -170,6 +95,11 @@ class JobBase extends ContainerBase implements JobInterface {
   // Holds the name and Docker IDs of our executable containers.
   public $executableContainers = [];
 
+  // Holds our Docker container manager
+  protected $docker;
+
+  // Holds build variables which need to be persisted between build steps
+  public $buildVars = array();
 
   // Holds our DrupalCIResultsAPI API
   protected $resultsAPI = NULL;
@@ -242,8 +172,66 @@ class JobBase extends ContainerBase implements JobInterface {
     return $this->resultsServerID;
   }
 
+  /**
+   * Stores the calling command's output buffer
+   *
+   * @var \Symfony\Component\Console\Output\OutputInterface
+   */
+  public $output;
+
+  public function getBuildVars() {
+    return $this->buildVars;
+  }
+
+  // Sets the build variables for this job
+  public function setBuildVars(array $build_vars) {
+    $this->buildVars = $build_vars;
+  }
+
+  // Retrieves a single build variable for this job
+  public function getBuildvar($build_var) {
+    return isset($this->buildVars[$build_var]) ? $this->buildVars[$build_var] : NULL;
+  }
+
+  // Sets a single build variable for this job
+  public function setBuildVar($build_var, $value) {
+    $this->buildVars[$build_var] = $value;
+  }
+
+  public function getRequiredArguments() {
+    return $this->requiredArguments;
+  }
+
+  public function setOutput(OutputInterface $output) {
+    $this->output = $output;
+  }
+
+  public function getOutput() {
+    return $this->output;
+  }
+
+  public function getDefinition() {
+    return $this->jobDefinition;
+  }
+
+  public function setDefinition(array $job_definition) {
+    $this->jobDefinition = $job_definition;
+  }
 
+  public function getDefinitionFile() {
+    return $this->jobDefinitionFile;
+  }
 
+  public function setDefinitionFile($filename) {
+    $this->jobDefinitionFile = $filename;
+  }
+  public function getDefaultArguments() {
+    return $this->defaultArguments;
+  }
+
+  public function getPlatformDefaults() {
+    return $this->platformDefaults;
+  }
 
   public function getServiceContainers() {
     return $this->serviceContainers;
@@ -253,20 +241,17 @@ class JobBase extends ContainerBase implements JobInterface {
     $this->serviceContainers = $service_containers;
   }
 
-  public function error() {
-    $results = $this->getJobResults();
-    $stage = $results->getCurrentStage();
-    $step = $results->getCurrentStep();
-    $results->setResultByStage($stage, 'Error');
-    $results->setResultByStep($stage, $step, 'Error');
+  public function getWorkingDir() {
+    return $this->workingDirectory;
+  }
+
+  public function setWorkingDir($working_directory) {
+    $this->workingDirectory = $working_directory;
   }
 
-  public function fail() {
-    $results = $this->getJobResults();
-    $stage = $results->getCurrentStage();
-    $step = $results->getCurrentStep();
-    $results->setResultByStage($stage, 'Fail');
-    $results->setResultByStep($stage, $step, 'Fail');
+  public function errorOutput($type = 'Error', $message = 'DrupalCI has encountered an error.') {
+    Output::error($type, $message);
+    $this->errorStatus = -1;
   }
 
   public function shellCommand($cmd) {
@@ -314,6 +299,14 @@ class JobBase extends ContainerBase implements JobInterface {
     return $this->plugins[$type][$plugin_id];
   }
 
+  public function getDocker()
+  {
+    $client = Client::createWithEnv();
+    if (null === $this->docker) {
+      $this->docker = new Docker($client);
+    }
+    return $this->docker;
+  }
 
   public function getExecContainers() {
     $configs = $this->executableContainers;
@@ -385,7 +378,7 @@ class JobBase extends ContainerBase implements JobInterface {
   protected function createContainerVolumes(&$config) {
     $volumes = array();
     // Map working directory
-    $working = $this->getJobCodebase()->getWorkingDir();
+    $working = $this->workingDirectory;
     $mount_point = (empty($config['Mountpoint'])) ? "/data" : $config['Mountpoint'];
     $config['HostConfig']['Binds'][] = "$working:$mount_point";
   }
@@ -470,8 +463,7 @@ class JobBase extends ContainerBase implements JobInterface {
   }
 
   public function getErrorState() {
-    $results = $this->getJobResults();
-    return ($results->getResultByStep($results->getCurrentStage(), $results->getCurrentStep()) === "Error");
+    return $this->errorStatus;
   }
 
   public function getArtifactList($include = array()) {
@@ -587,34 +579,6 @@ class JobBase extends ContainerBase implements JobInterface {
     return $this->artifactDirectory;
   }
 
-  /**
-   * Returns the default job definition template for this job type
-   *
-   * This method may be overridden by a specific job class to add template
-   * selection logic, if desired.
-   *
-   * @param $job_type
-   *   The name of the job type, used to select the appropriate subdirectory
-   *
-   * @return string
-   *   The location of the default job definition template
-   */
-  public function getDefaultDefinitionTemplate($job_type) {
-    return __DIR__ . "/$job_type/drupalci.yml";
-  }
 
-  /**
-   * Generate a Build ID for this job
-   */
-  public function generateBuildId() {
-    // Use the BUILD_TAG environment variable if present, otherwise generate a
-    // unique build tag based on timestamp.
-    $build_id = getenv('BUILD_TAG');
-    if (empty($build_id)) {
-      $build_id = $this->getJobType() . '_' . time();
-    }
-    $this->setBuildId($build_id);
-    Output::writeLn("<info>Executing job with build ID: <options=bold>$build_id</options=bold></info>");
-  }
 
 }
diff --git a/src/DrupalCI/Plugin/JobTypes/JobInterface.php b/src/DrupalCI/Plugin/JobTypes/JobInterface.php
index 2415c88..9f88314 100644
--- a/src/DrupalCI/Plugin/JobTypes/JobInterface.php
+++ b/src/DrupalCI/Plugin/JobTypes/JobInterface.php
@@ -5,90 +5,40 @@
  */
 namespace DrupalCI\Plugin\JobTypes;
 
-use DrupalCI\Job\CodeBase\JobCodebase;
-use DrupalCI\Job\Definition\JobDefinition;
-use DrupalCI\Job\Results\JobResults;
 use Symfony\Component\Console\Output\OutputInterface;
 
 interface JobInterface {
 
   /**
-   * @return string
-   */
-  public function getJobType();
-
-  /**
-   * @return \Symfony\Component\Console\Output\OutputInterface
-   */
-  public function getOutput();
-
-  /**
-   * @param \Symfony\Component\Console\Output\OutputInterface $output
-   */
-  public function setOutput(OutputInterface $output);
-
-  /**
-   * @return string
-   */
-  public function getBuildId();
-
-  /**
-   * @param string
-   */
-  public function setBuildId($id);
-
-  /**
-   * @return \DrupalCI\Job\Definition\JobDefinition
-   */
-  public function getJobDefinition();
-
-  /**
-   * @param \DrupalCI\Job\Definition\JobDefinition $job_definition
-   */
-  public function setJobDefinition(JobDefinition $job_definition);
-
-  /**
-   * @return \DrupalCI\Job\CodeBase\JobCodebase
-   */
-  public function getJobCodebase();
-
-  /**
-   * @param \DrupalCI\Job\CodeBase\JobCodebase $job_codebase
-   */
-  public function setJobCodebase(JobCodebase $job_codebase);
-
-  /**
-   * @return \DrupalCI\Job\Results\JobResults
+   * An array of build variables.
+   *
+   * @return array
+   *
+   * @see SimpletestJob::$availableArguments
    */
-  public function getJobResults();
+  public function getBuildVars();
 
   /**
-   * @param \DrupalCI\Job\Results\JobResults $job_results
+   * @param array $build_vars
+   *
+   * @see JobInterface::getBuildvards
    */
-  public function setJobResults(JobResults $job_results);
-
+  public function setBuildVars(array $build_vars);
 
   /**
-   * Available arguments.
-   *
-   * @TODO: move to annotation
+   * @param string $build_var
    *
-   * @return array
+   * @return mixed
    *
-   * @see SimpletestJob::$availableArguments
+   * @see JobInterface::getBuildvards
    */
-  public function getAvailableArguments();
+  public function getBuildvar($build_var);
 
   /**
-   * Default arguments.
-   *
-   * @TODO: move to annotation
-   *
-   * @return array
-   *
-   * @see SimpletestJob::$defaultArguments
+   * @param $build_var
+   * @param $value
    */
-  public function getDefaultArguments();
+  public function setBuildVar($build_var, $value);
 
   /**
    * Required arguments.
@@ -102,40 +52,23 @@ interface JobInterface {
   public function getRequiredArguments();
 
   /**
-   * An array of build variables.
-   *
-   * @return array
-   *
-   * @see SimpletestJob::$availableArguments
+   * @return \Symfony\Component\Console\Output\OutputInterface
    */
-  public function getBuildVars();
+  public function getOutput();
 
   /**
-   * @param array $build_vars
-   *
-   * @see JobInterface::getBuildvars
+   * @param \Symfony\Component\Console\Output\OutputInterface $output
    */
-  public function setBuildVars(array $build_vars);
+  public function setOutput(OutputInterface $output);
 
   /**
-   * @param string $build_var
+   * Sends an error message.
    *
+   * @param string $type
+   * @param string $message
    * @return mixed
-   *
-   * @see JobInterface::getBuildvars
    */
-  public function getBuildVar($build_var);
-
-  /**
-   * @param $build_var
-   * @param $value
-   */
-  public function setBuildVar($build_var, $value);
-
-  /**
-   * @return \Docker\Docker
-   */
-  public function getDocker();
+  public function errorOutput($type = 'Error', $message = 'DrupalCI has encountered an error.');
 
   /**
    * Execute a shell command.
@@ -147,6 +80,11 @@ interface JobInterface {
    */
   public function shellCommand($cmd);
 
+  /**
+   * @return \Docker\Docker
+   */
+  public function getDocker();
+
   public function configureResultsAPI($config);
   /**
    * Get a list of containers to run Docker exec in.
@@ -167,7 +105,15 @@ interface JobInterface {
 
   public function getErrorState();
 
+  public function getDefinition();
+
+  public function setDefinition(array $job_definition);
 
+  public function getDefinitionFile();
+
+  public function setDefinitionFile($filename);
+
+  public function getDefaultArguments();
 
   public function getPlatformDefaults();
 
@@ -175,6 +121,13 @@ interface JobInterface {
 
   public function setServiceContainers(array $service_containers);
 
+  public function getWorkingDir();
+
+  public function setWorkingDir($working_directory);
+
+  public function setBuildId($id);
+
+  public function getBuildId();
 
   public function setResultsServerID($id);
 
@@ -199,12 +152,4 @@ interface JobInterface {
 
   public function setArtifactDirectory($directory);
 
-  public function getDefaultDefinitionTemplate($job_type);
-
-
-  public function generateBuildId();
-
-  public function error();
-
-  public function fail();
 }
\ No newline at end of file
diff --git a/src/DrupalCI/Plugin/JobTypes/generic/GenericJob.php b/src/DrupalCI/Plugin/JobTypes/generic/GenericJob.php
index 3d88662..78a7ea2 100644
--- a/src/DrupalCI/Plugin/JobTypes/generic/GenericJob.php
+++ b/src/DrupalCI/Plugin/JobTypes/generic/GenericJob.php
@@ -17,26 +17,14 @@ use DrupalCI\Plugin\JobTypes\JobBase;
  */
 
 class GenericJob extends JobBase {
-
-  /**
-   * @var string
-   */
-  public $jobType = 'generic';
-
   /**
-   * Overrides the getDefaultDefinitionTemplate() method from within JobBase.
-   *
-   * For 'generic' job types, if no file is provided, we assume the presence of
-   * a drupalci.yml file in the current working directory.
+   * Job Type (jobType)
    *
-   * @param $job_type
-   *   The name of the job type, used to select the appropriate subdirectory
+   * @var string
    *
-   * @return string
-   *   The location of the default job definition template
+   * This property is not referenced in the current code, but it is anticipated
+   * that others may want to reference the job type from the object itself at
+   * some point in the future.
    */
-  public function getDefaultDefinitionTemplate($job_type) {
-    return "./drupalci.yml";
-  }
-
+  public $jobtype = 'generic';
 }
\ No newline at end of file
diff --git a/src/DrupalCI/Plugin/JobTypes/phpunit/PHPUnitJob.php b/src/DrupalCI/Plugin/JobTypes/phpunit/PHPUnitJob.php
index 04702f2..8f6abda 100644
--- a/src/DrupalCI/Plugin/JobTypes/phpunit/PHPUnitJob.php
+++ b/src/DrupalCI/Plugin/JobTypes/phpunit/PHPUnitJob.php
@@ -16,9 +16,15 @@ use DrupalCI\Plugin\JobTypes\JobBase;
 class PHPUnitJob extends JobBase {
 
   /**
+   * Job Type (jobType)
+   *
    * @var string
+   *
+   * This property is not referenced in the current code, but it is anticipated
+   * that others may want to reference the job type from the object itself at
+   * some point in the future.
    */
-  public $jobType = 'phpunit';
+  public $jobtype = 'phpunit';
 
   /**
    * Default Arguments (defaultArguments)
@@ -82,7 +88,7 @@ class PHPUnitJob extends JobBase {
   public $availableArguments = array(
     // ***** Variables Available for any job type *****
     'DCI_UseLocalCodebase' => 'Used to define a local codebase to be cloned (instead of performing a Git checkout)',
-    'DCI_WorkingDir' => 'Defines the location to be used in creating the local copy of the codebase, to be mapped into the container as a container volume.  Default: /tmp/simpletest-[random string]',
+    'DCI_CheckoutDir' => 'Defines the location to be used in creating the local copy of the codebase, to be mapped into the container as a container volume.  Default: /tmp/simpletest-[random string]',
     'DCI_ResultsServer' => 'Specifies the url string of a DrupalCI results server for which to publish job results',
     'DCI_ResultsServerConfig' => 'Specifies the location of a configuration file on the test runner containg a DrupalCI Results Server configuration to use in publishing results.',
     'DCI_JobBuildId' => 'Specifies a unique build ID assigned to this job from an upstream server',
diff --git a/src/DrupalCI/Plugin/JobTypes/simpletest/SimpletestJob.php b/src/DrupalCI/Plugin/JobTypes/simpletest/SimpletestJob.php
index dfb402e..9664b0d 100644
--- a/src/DrupalCI/Plugin/JobTypes/simpletest/SimpletestJob.php
+++ b/src/DrupalCI/Plugin/JobTypes/simpletest/SimpletestJob.php
@@ -16,8 +16,14 @@ use DrupalCI\Plugin\JobTypes\JobBase;
 class SimpletestJob extends JobBase {
 
   /**
+   * Job Type (jobType)
+   *
    * @var string
-  */
+   *
+   * This property is not referenced in the current code, but it is anticipated
+   * that others may want to reference the job type from the object itself at
+   * some point in the future.
+   */
   public $jobType = 'simpletest';
 
   /**
@@ -93,7 +99,7 @@ class SimpletestJob extends JobBase {
   public $availableArguments = array(
     // ***** Variables Available for any job type *****
     'DCI_UseLocalCodebase' => 'Used to define a local codebase to be cloned (instead of performing a Git checkout)',
-    'DCI_WorkingDir' => 'Defines the location to be used in creating the local copy of the codebase, to be mapped into the container as a container volume.  Default: /tmp/simpletest-[random string]',
+    'DCI_CheckoutDir' => 'Defines the location to be used in creating the local copy of the codebase, to be mapped into the container as a container volume.  Default: /tmp/simpletest-[random string]',
     'DCI_ResultsServer' => 'Specifies the url string of a DrupalCI results server for which to publish job results',
     'DCI_ResultsServerConfig' => 'Specifies the location of a configuration file on the test runner containg a DrupalCI Results Server configuration to use in publishing results.',
     'DCI_JobBuildId' => 'Specifies a unique build ID assigned to this job from an upstream server',
@@ -139,8 +145,8 @@ class SimpletestJob extends JobBase {
    * any other DCI_* variable preprocessors are executed.
    */
   public $priorityArguments = array(
-    // CoreProject doesn't *need* to run first, but it seems like a good idea
-    'DCI_CoreProject',
+    // IsDrupal doesn't *need* to run first, but it seems like a good idea
+    'DCI_IsDrupal',
     // Expand run options to their argument format, before adding arguments
     'DCI_RunOptions',
     // CoreBranch needs to be able to override the SQLite variable before the
diff --git a/src/DrupalCI/Plugin/Preprocess/variable/RunOptions.php b/src/DrupalCI/Plugin/Preprocess/variable/RunOptions.php
index 45cb3e8..2394079 100644
--- a/src/DrupalCI/Plugin/Preprocess/variable/RunOptions.php
+++ b/src/DrupalCI/Plugin/Preprocess/variable/RunOptions.php
@@ -28,7 +28,6 @@ class RunOptions extends PluginBase {
     $expanded = explode(';', $arguments);
     $parsed = "";
     foreach ($expanded as $argument_string) {
-      if (empty($argument_string)) { continue; }
       if (strpos($argument_string, ',') === FALSE) {
         $parsed .= " --" . $argument_string;
       }
