diff --git a/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.features.field.inc b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.features.field.inc
new file mode 100644
index 0000000..ec2e225
--- /dev/null
+++ b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.features.field.inc
@@ -0,0 +1,208 @@
+<?php
+/**
+ * @file
+ * drupalorg_project_issue_tasks.features.field.inc
+ */
+
+/**
+ * Implements hook_field_default_fields().
+ */
+function drupalorg_project_issue_tasks_field_default_fields() {
+  $fields = array();
+
+  // Exported field: 'node-project_issue-field_issue_tasks'.
+  $fields['node-project_issue-field_issue_tasks'] = array(
+    'field_config' => array(
+      'active' => '1',
+      'cardinality' => '-1',
+      'deleted' => '0',
+      'entity_types' => array(),
+      'field_name' => 'field_issue_tasks',
+      'foreign keys' => array(
+        'taxonomy_term_data' => array(
+          'columns' => array(
+            'target_id' => 'tid',
+          ),
+          'table' => 'taxonomy_term_data',
+        ),
+      ),
+      'indexes' => array(
+        'target_id' => array(
+          0 => 'target_id',
+        ),
+      ),
+      'locked' => '0',
+      'module' => 'entityreference',
+      'settings' => array(
+        'handler' => 'base',
+        'handler_settings' => array(
+          'behaviors' => array(
+            'views-select-list' => array(
+              'status' => 0,
+            ),
+          ),
+          'sort' => array(
+            'type' => 'none',
+          ),
+          'target_bundles' => array(
+            'issue_tasks' => 'issue_tasks',
+          ),
+        ),
+        'target_type' => 'taxonomy_term',
+      ),
+      'translatable' => '0',
+      'type' => 'entityreference',
+    ),
+    'field_instance' => array(
+      'bundle' => 'project_issue',
+      'default_value' => NULL,
+      'deleted' => '0',
+      'description' => '',
+      'display' => array(
+        'default' => array(
+          'label' => 'above',
+          'module' => 'entityreference',
+          'settings' => array(
+            'link' => 1,
+          ),
+          'type' => 'entityreference_label',
+          'weight' => '51',
+        ),
+        'diff_standard' => array(
+          'label' => 'above',
+          'settings' => array(),
+          'type' => 'hidden',
+          'weight' => 0,
+        ),
+        'issuemetadata' => array(
+          'label' => 'above',
+          'module' => 'entityreference',
+          'settings' => array(
+            'link' => 1,
+          ),
+          'type' => 'entityreference_label',
+          'weight' => '7',
+        ),
+        'nodechanges' => array(
+          'label' => 'above',
+          'settings' => array(),
+          'type' => 'hidden',
+          'weight' => 0,
+        ),
+        'teaser' => array(
+          'label' => 'above',
+          'settings' => array(),
+          'type' => 'hidden',
+          'weight' => 0,
+        ),
+      ),
+      'entity_type' => 'node',
+      'field_name' => 'field_issue_tasks',
+      'label' => 'Issue tasks',
+      'required' => 0,
+      'settings' => array(
+        'behaviors' => array(
+          'taxonomy-index' => array(
+            'status' => TRUE,
+          ),
+        ),
+        'user_register_form' => FALSE,
+      ),
+      'widget' => array(
+        'active' => 1,
+        'module' => 'options',
+        'settings' => array(),
+        'type' => 'options_select',
+        'weight' => '6',
+      ),
+    ),
+  );
+
+  // Exported field: 'taxonomy_term-issue_tasks-field_issue_task_instructions'.
+  $fields['taxonomy_term-issue_tasks-field_issue_task_instructions'] = array(
+    'field_config' => array(
+      'active' => '1',
+      'cardinality' => '1',
+      'deleted' => '0',
+      'entity_types' => array(),
+      'field_name' => 'field_issue_task_instructions',
+      'foreign keys' => array(
+        'node' => array(
+          'columns' => array(
+            'target_id' => 'nid',
+          ),
+          'table' => 'node',
+        ),
+      ),
+      'indexes' => array(
+        'target_id' => array(
+          0 => 'target_id',
+        ),
+      ),
+      'locked' => '0',
+      'module' => 'entityreference',
+      'settings' => array(
+        'handler' => 'base',
+        'handler_settings' => array(
+          'behaviors' => array(
+            'views-select-list' => array(
+              'status' => 0,
+            ),
+          ),
+          'sort' => array(
+            'type' => 'none',
+          ),
+          'target_bundles' => array(
+            'book' => 'book',
+          ),
+        ),
+        'target_type' => 'node',
+      ),
+      'translatable' => '0',
+      'type' => 'entityreference',
+    ),
+    'field_instance' => array(
+      'bundle' => 'issue_tasks',
+      'default_value' => NULL,
+      'deleted' => '0',
+      'description' => 'A reference to a Drupal Handbook document with instructions for completing the task.',
+      'display' => array(
+        'default' => array(
+          'label' => 'above',
+          'module' => 'entityreference',
+          'settings' => array(
+            'link' => 1,
+          ),
+          'type' => 'entityreference_label',
+          'weight' => '0',
+        ),
+      ),
+      'entity_type' => 'taxonomy_term',
+      'field_name' => 'field_issue_task_instructions',
+      'label' => 'Task instructions',
+      'required' => 0,
+      'settings' => array(
+        'user_register_form' => FALSE,
+      ),
+      'widget' => array(
+        'active' => 1,
+        'module' => 'entityreference',
+        'settings' => array(
+          'match_operator' => 'STARTS_WITH',
+          'path' => '',
+          'size' => '60',
+        ),
+        'type' => 'entityreference_autocomplete',
+        'weight' => '1',
+      ),
+    ),
+  );
+
+  // Translatables
+  // Included for use with string extractors like potx.
+  t('A reference to a Drupal Handbook document with instructions for completing the task.');
+  t('Issue tasks');
+  t('Task instructions');
+
+  return $fields;
+}
diff --git a/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.features.taxonomy.inc b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.features.taxonomy.inc
new file mode 100644
index 0000000..b41011e
--- /dev/null
+++ b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.features.taxonomy.inc
@@ -0,0 +1,21 @@
+<?php
+/**
+ * @file
+ * drupalorg_project_issue_tasks.features.taxonomy.inc
+ */
+
+/**
+ * Implements hook_taxonomy_default_vocabularies().
+ */
+function drupalorg_project_issue_tasks_taxonomy_default_vocabularies() {
+  return array(
+    'issue_tasks' => array(
+      'name' => 'Issue tasks',
+      'machine_name' => 'issue_tasks',
+      'description' => 'Tasks that need to be completed for a Drupal issue',
+      'hierarchy' => '0',
+      'module' => 'taxonomy',
+      'weight' => '0',
+    ),
+  );
+}
diff --git a/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.info b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.info
new file mode 100644
index 0000000..c8b28df
--- /dev/null
+++ b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.info
@@ -0,0 +1,17 @@
+name = Drupal.org issue tasks
+description = Adds the management of issue tasks to project_issue nodes.
+core = 7.x
+package = Features
+php = 5.2.4
+version = 7.x-3.x
+project = drupalorg_project_issue_tasks
+dependencies[] = entityreference
+dependencies[] = features
+dependencies[] = options
+dependencies[] = project_issue
+dependencies[] = taxonomy
+features[features_api][] = api:1
+features[field][] = node-project_issue-field_issue_tasks
+features[field][] = taxonomy_term-issue_tasks-field_issue_task_instructions
+features[taxonomy][] = issue_tasks
+project status url = http://drupal.org
diff --git a/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.install b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.install
new file mode 100644
index 0000000..409b229
--- /dev/null
+++ b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.install
@@ -0,0 +1,145 @@
+<?php
+/**
+ * @file
+ * Install and update functions for drupalorg_project_issue_tasks.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function drupalorg_project_issue_tasks_install() {
+  // Set the installed schema version so our update can be run manually.
+  drupal_set_installed_schema_version('drupalorg_project_issue_tasks', 7000);
+}
+
+/**
+ * Create issue task terms and add them to issue nodes with related tags.
+ *
+ * This update hook is chock full of Entity API calls, which we wouldn't allow
+ * in core, but since this is a one-off for Drupal.org it should be fine.
+ */
+function drupalorg_project_issue_tasks_update_7001() {
+  // Load existing issue_task terms and ensure we do not create duplicates.
+  // This shouldn't be necessary, but for some reason this update sometimes
+  // inexplicably starts over from the beginning on a d.o development server,
+  // so we need to ensure there aren't duplicate terms created.
+  $existing_tasks = array();
+  $tasks_q = new EntityFieldQuery();
+  $tasks_q
+    // Fetch all issue tasks terms.
+    ->entityCondition('entity_type', 'taxonomy_term')
+    ->entityCondition('bundle', 'issue_tasks')
+    // We want to check for all terms regardless of user access.
+    ->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');
+  $tasks_r = $tasks_q->execute();
+
+  // Collect the names of any existing terms in an array.
+  if (isset($tasks_r['taxonomy_term'])) {
+    $existing_task_data = taxonomy_term_load_multiple(array_keys($tasks_r['taxonomy_term']));
+    foreach ($existing_task_data as $task_term) {
+      $existing_tasks[] = $task_term->name;
+    }
+  }
+
+  // Saving terms with just the vocab machine name doesn't work in D7, and the
+  // above EFQ will have no results when update.php isn't acting crazy.
+  $vocab = taxonomy_vocabulary_machine_name_load('issue_tasks');
+
+  // Create issue_task terms for tasks that do not have tags.
+  $no_tag_terms = _drupalorg_project_issue_tasks_terms_no_tag();
+  foreach ($no_tag_terms as $tag_name => $term) {
+    if (!in_array($term->name, $existing_tasks)) {
+      $term->vid = $vocab->vid;
+      if (taxonomy_term_save($term)) {
+        watchdog('drupalorg', "Created issue task %name (ID %tid).", array('%name' => $term->name, '%tid' => $term->tid));
+      }
+      else {
+        watchdog('drupalorg', "Failed to save task %name.", array('%name' => $term->name));
+      }
+    }
+  }
+
+  // Create issue_task terms for existing issue tags.
+  $terms = _drupalorg_project_issue_tasks_terms_with_tag();
+  foreach ($terms as $tag_name => $term) {
+    if (in_array($term->name, $existing_tasks)) {
+      // Skip tasks that have already been created here and later.
+      unset($terms[$tag_name]);
+    }
+    else{
+      $term->vid = $vocab->vid;
+      $term->vid = $vocab->vid;
+      if (taxonomy_term_save($term)) {
+        watchdog('drupalorg', "Created issue task %name (ID %tid).", array('%name' => $term->name, '%tid' => $term->tid));
+      }
+      else {
+        watchdog('drupalorg', "Failed to save task %name.", array('%name' => $term->name));
+      }
+    }
+  }
+
+  // Only proceed with if new terms were created.
+  if (!empty($terms)) {
+    // Fetch term objects for the existing issue tags.
+    $tags_q = new EntityFieldQuery();
+    $tags_q
+      // Fetch the listed issue tags.
+      ->entityCondition('entity_type', 'taxonomy_term')
+      ->entityCondition('bundle', 'vocabulary_9')
+      ->propertyCondition('name', array_keys($terms), 'IN')
+      // We want to check for all terms regardless of user access.
+      ->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');
+    $tags_r = $tags_q->execute();
+
+    // If there are matching taxonomy terms, update the nodes tagged with them.
+    if (isset($tags_r['taxonomy_term'])) {
+      // Load the full tag objects because we need to know which is which.
+      $tags = taxonomy_term_load_multiple(array_keys($tags_r['taxonomy_term']));
+
+      // Look up nodes tagged with these tags using the index table.
+      $ti = db_query(
+        "SELECT n.nid, ti.tid
+         FROM {node} n
+         INNER JOIN {taxonomy_index} ti ON n.nid = ti.nid
+         WHERE ti.tid IN (:tags)",
+        array(':tags' => array_keys($tags)))
+        ->fetchAll(PDO::FETCH_ASSOC);
+
+      // Rearrage the results into a nested array keyed by nid.
+      $ids = array();
+      foreach ($ti as $entry) {
+        $ids[$entry['nid']][] = $entry['tid'];
+      }
+
+      // Load all the nodes.
+      $nodes = node_load_multiple(array_keys($ids));
+
+      // Add the tasks to each node and keep count.
+      $i = 0;
+      foreach ($nodes as $node) {
+        $update = FALSE;
+        foreach ($ids[$node->nid] as $tid) {
+          $new_task = $terms[strtolower($tags[$tid]->name)];
+          if (!empty($new_task)) {
+            $update = TRUE;
+            $node->field_issue_tasks[LANGUAGE_NONE][] = array('target_id' => $new_task->tid);
+          }
+        }
+        // Only update the node if we are adding at least one task.
+        if ($update) {
+          node_save($node);
+          $i++;
+        }
+      }
+
+      watchdog('drupalorg', "Updated @count nodes.", array('@count' => $i));
+
+      // Delete the tags.
+      // @todo This is commented because it is destructive and we might want
+      //   to wait and do this manually once documentation is updated.
+      // foreach ($tags as $tid => $tag) {
+      //   taxonomy_term_delete($tid);
+      // }
+    }
+  }
+}
diff --git a/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.module b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.module
new file mode 100644
index 0000000..1a831f0
--- /dev/null
+++ b/features/drupalorg_project_issue_tasks/drupalorg_project_issue_tasks.module
@@ -0,0 +1,335 @@
+<?php
+/**
+ * @file
+ * Drupal needs this blank file.
+ */
+
+function drupalorg_project_issue_tasks_init() {
+}
+function foo() {
+}
+
+/**
+ * Returns an array to create taxonomy terms for issue tasks and tag issues.
+ *
+ * @return array
+ *   An array of taxonomy term objects for taxonomy_term_save(), keyed by the
+ *   existing issue tag name that the task term replaces.
+ */
+function _drupalorg_project_issue_tasks_terms_with_tag() {
+  $terms = array();
+  $terms['needs accessibility review'] = (object) array(
+    'name' => 'Accessibility review',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1853532',
+        ),
+      ),
+    ),
+  );
+  $terms['needs tests'] = (object) array(
+    'name' => 'Automated tests',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1468170',
+        ),
+      ),
+    ),
+  );
+  $terms['needs change notification'] = (object) array(
+    'name' => 'Change record',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1487226',
+        ),
+      ),
+    ),
+  );
+  $terms['needs committer feedback'] = (object) array(
+    'name' => 'Committer feedback',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+    ),
+  );
+  $terms['needs followup'] = (object) array(
+    'name' => 'Create followup issue',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+    ),
+  );
+  $terms['needs documentation'] = (object) array(
+    'name' => 'Improve documentation',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1487976',
+        ),
+      ),
+    ),
+  );
+  $terms['needs issue summary update'] = (object) array(
+    'name' => 'Issue summary update',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1427826',
+        ),
+      ),
+    ),
+  );
+  $terms['needs javascript review'] = (object) array(
+    'name' => 'JavaScript review',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+    ),
+  );
+  $terms['needs manual testing'] = (object) array(
+    'name' => 'Manual testing',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1489010',
+        ),
+      ),
+    ),
+  );
+  $terms['needs profiling'] = (object) array(
+    'name' => 'Profiling',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+    ),
+  );
+  $terms['needs reroll'] = (object) array(
+    'name' => 'Reroll',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1590202',
+        ),
+      ),
+    ),
+  );
+  $terms['needs screenshot'] = (object) array(
+    'name' => 'Screenshots',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1859584',
+        ),
+      ),
+    ),
+  );
+  $terms['needs security review'] = (object) array(
+    'name' => 'Security review',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+    ),
+  );
+  $terms['needs steps to reproduce'] = (object) array(
+    'name' => 'Steps to reproduce',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1468198',
+        ),
+      ),
+    ),
+  );
+  $terms['needs upgrade path'] = (object) array(
+    'name' => 'Upgrade path',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+    ),
+  );
+  $terms['needs upgrade path tests'] = (object) array(
+    'name' => 'Upgrade path tests',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1429136',
+        ),
+      ),
+    ),
+  );
+  $terms['needs usability review'] = (object) array(
+    'name' => 'Usability review',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+    ),
+  );
+
+  return $terms;
+}
+
+/**
+ * Returns an array to create taxonomy terms for issue tasks.
+ *
+ * @return array
+ *   A flat array of taxonomy term objects for taxonomy_term_save(), for terms
+ *   that do not correspond to existing issue tags.
+ */
+function _drupalorg_project_issue_tasks_terms_no_tag() {
+  $terms = array();
+  // This tag doesn't exist; it's usually implicit from "Needs review".
+  // However, there are different kinds of reviews, so allow the user to
+  // specify they specifically want a line-by-line code review.
+  $terms[] = (object) array(
+    'name' => 'Code review',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1488992',
+        ),
+      ),
+    ),
+  );
+  // This tag doesn't exist, but we want a task for it.
+  $terms[] = (object) array(
+    'name' => 'Improve patch standards',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1487976',
+        ),
+      ),
+    ),
+  );
+  // The 'Backport' task is added in addition to, but does not replace, the
+  // "Needs backport to D6" and "Needs backport to D7" tags. Those are used
+  // differently than other "Needs" tags, to permanently track issues that have
+  // been backported as opposed to issues that are waiting for a backport task.
+  $terms[] = (object) array(
+    'name' => 'Backport',
+    'description' => '',
+    'format' => '1',
+    'weight' => '0',
+    'vocabulary_machine_name' => 'issue_tasks',
+    'field_issue_task_instructions' =>
+    array (
+      'und' =>
+      array (
+        0 =>
+        array (
+          'target_id' => '1538380',
+        ),
+      ),
+    ),
+  );
+
+  return $terms;
+}
