diff --git includes/form.inc includes/form.inc
index 88ff9ab..d28e24c 100644
--- includes/form.inc
+++ includes/form.inc
@@ -94,7 +94,10 @@ function drupal_get_form($form_id) {
  * @param &$form_state
  *   An array which stores information about the form. This is passed as a
  *   reference so that the caller can use it to examine what in the form changed
- *   when the form submission process is complete.
+ *   when the form submission process is complete. Furthermore it may be used
+ *   to store information related to the processed data in the form. The data
+ *   put into $form_state is persisting across page requests, thus it can be
+ *   accessed and used during the whole form workflow. 
  *   The following parameters may be set in $form_state to affect how the form
  *   is rendered:
  *   - build_info: A keyed array of build information that is necessary to
@@ -104,10 +107,6 @@ function drupal_get_form($form_id) {
  *     - file: An optional include file that contains the form and is
  *       automatically loaded by form_get_cache(). Defaults to the current menu
  *       router item's 'file' definition, if existent.
- *   - storage: An array that may be used to store information related to the
- *     processed data in the form, which can be accessed and used within the
- *     same page request, but also persist across multiple steps in a multi-step
- *     form.
  *   - rebuild: Normally, after the entire form processing is completed and
  *     submit handlers ran, a form is considered to be done and
  *     drupal_redirect_form() will redirect the user to a new page using a GET
@@ -141,6 +140,10 @@ function drupal_get_form($form_id) {
  *     times when a form is resubmitted internally and should be validated
  *     again. Setting this to TRUE will force that to happen. This is most
  *     likely to occur during AHAH or AJAX operations.
+ *   - temporary: An array holding temporary data accessible during the current
+ *     page request only. It may be used to temporary save any data that doesn't
+ *     need to or shouldn't be cached during the whole form workflow, e.g. data
+ *     that needs to be accessed during the current form build process only.
  *   - wrapper_callback: Modules that wish to pre-populate certain forms with
  *     common elements, such as back/next/save buttons in multi-step form
  *     wizards, may define a form builder function name that returns a form
@@ -269,11 +272,10 @@ function drupal_build_form($form_id, &$form_state) {
  */
 function form_state_defaults() {
   return array(
-    'args' => array(),
     'rebuild' => FALSE,
     'redirect' => NULL,
-    'build_info' => array(),
-    'storage' => array(),
+    'build_info' => array('args' => array()),
+    'temporary' => array(),
     'submitted' => FALSE,
     'programmed' => FALSE,
     'cache'=> FALSE,
@@ -301,8 +303,7 @@ function form_state_defaults() {
  *   different $form_id values to the proper form constructor function. Examples
  *   may be found in node_forms(), search_forms(), and user_forms().
  * @param $form_state
- *   A keyed array containing the current state of the form. Most
- *   important is the $form_state['storage'] collection.
+ *   A keyed array containing the current state of the form.
  * @param $form_build_id
  *   If the AHAH callback calling this function only alters part of the form,
  *   then pass in the existing form_build_id so we can re-cache with the same
@@ -355,8 +356,7 @@ function form_get_cache($form_build_id, &$form_state) {
     if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {
       if ($cached = cache_get('form_state_' . $form_build_id, 'cache_form')) {
         // Re-populate $form_state for subsequent rebuilds.
-        $form_state['build_info'] = $cached->data['build_info'];
-        $form_state['storage'] = $cached->data['storage'];
+        $form_state = $cached->data + $form_state;
 
         // If the original form is contained in an include file, load the file.
         // @see drupal_build_form()
@@ -385,16 +385,40 @@ function form_set_cache($form_build_id, $form, $form_state) {
   }
 
   // Cache form state.
-  if (!empty($form_state['build_info']) || !empty($form_state['storage'])) {
-    $data = array(
-      'build_info' => $form_state['build_info'],
-      'storage' => $form_state['storage'],
-    );
+  if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
     cache_set('form_state_' . $form_build_id, $data, 'cache_form', REQUEST_TIME + $expire);
   }
 }
 
 /**
+ * Returns an array of $form_state keys that shouldn't be cached.
+ */
+function form_state_keys_no_cache() {
+  return array(
+    // Public properties defined by form constructors and form handlers.
+    'cache',
+    'no_cache',
+    'must_validate',
+    'redirect',
+    'no_redirect',
+    'rebuild',
+    'always_process',
+    'temporary',
+    // Internal properties defined by form processing.
+    'clicked_button',
+    'buttons',
+    'complete form',
+    'groups',
+    'input',
+    'method',
+    'submit_handlers',
+    'submitted',
+    'validate_handlers',
+    'values',
+  );
+}
+
+/**
  * Retrieves a form using a form_id, populates it with $form_state['values'],
  * processes it, and returns any validation errors encountered. This
  * function is the programmatic counterpart to drupal_get_form().
@@ -449,14 +473,14 @@ function drupal_form_submit($form_id, &$form_state) {
     array_shift($args);
     $form_state['build_info']['args'] = $args;
   }
+  // Merge in default values.
+  $form_state += form_state_defaults();
 
   $form = drupal_retrieve_form($form_id, $form_state);
   $form_state['input'] = $form_state['values'];
   $form_state['programmed'] = TRUE;
   // Programmed forms are always submitted.
   $form_state['submitted'] = TRUE;
-  // Merge in default values.
-  $form_state += form_state_defaults();
 
   drupal_prepare_form($form_id, $form, $form_state);
   drupal_process_form($form_id, $form, $form_state);
diff --git modules/simpletest/tests/form.test modules/simpletest/tests/form.test
index c1ab839..f82b482 100644
--- modules/simpletest/tests/form.test
+++ modules/simpletest/tests/form.test
@@ -541,6 +541,19 @@ class FormsFormStorageTestCase extends DrupalWebTestCase {
     $this->assertFieldByName('title', 'title_changed', t('The altered form storage value was updated in cache and taken over.'));
     $this->assertText('Title: title_changed', t('The form storage has stored the values.'));
   }
+  
+  /**
+   * Tests a form using form state without using 'storage' to pass data from the
+   * constructor to a submit handler. The data has to persist even when caching
+   * gets activated, what may happen when a modules alter the form and adds
+   * #ajax properties.
+   */
+  function testFormStatePersist() {
+    $this->drupalPost('form-test/state-persist', array(), t('Submit'));
+    // The submit handler outputs the value in $form_state, assert it's there.
+    $this->assertText('test');
+  }
+
 }
 
 /**
diff --git modules/simpletest/tests/form_test.module modules/simpletest/tests/form_test.module
index cdb6118..ceb3e46 100644
--- modules/simpletest/tests/form_test.module
+++ modules/simpletest/tests/form_test.module
@@ -94,6 +94,14 @@ function form_test_menu() {
     'type' => MENU_CALLBACK,
   );
 
+  $items['form-test/state-persist'] = array(
+    'title' => 'Form state persistence without storage',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('form_test_state_persist'),
+    'access callback' => TRUE,
+    'type' => MENU_CALLBACK,
+  );
+
   return $items;
 }
 
@@ -678,3 +686,36 @@ function form_test_form_rebuild_preserve_values_form_submit($form, &$form_state)
   // Finish the workflow. Do not rebuild.
   drupal_set_message(t('Form values: %values', array('%values' => var_export($form_state['values'], TRUE))));
 }
+
+/**
+ * Form constructor for testing form state persistence.
+ */
+function form_test_state_persist($form, &$form_state) {
+  $form['title'] = array(
+    '#type' => 'textfield',
+    '#title' => 'title',
+    '#default_value' => 'DEFAULT',
+    '#required' => TRUE,
+  );
+  $form_state['value'] = 'test';
+  
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit')
+  );
+  return $form;
+}
+
+function form_test_state_persist_submit($form, &$form_state) {
+  drupal_set_message($form_state['value']);
+ $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * Implement hook_form_FORM_ID_alter().
+ */
+function form_test_form_form_test_state_persist_alter(&$form, &$form_state) {
+  // Simulate a form alter implementation inserting form elements that enable
+  // caching of the form, e.g. elements having #ajax.
+  $form_state['cache'] = TRUE;
+}
diff --git modules/system/system.admin.inc modules/system/system.admin.inc
index e95fb95..753e9ab 100644
--- modules/system/system.admin.inc
+++ modules/system/system.admin.inc
@@ -1181,7 +1181,7 @@ function system_modules_uninstall($form, $form_state = NULL) {
   include_once DRUPAL_ROOT . '/includes/install.inc';
 
   // Display the confirm form if any modules have been submitted.
-  if (!empty($form_state) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
+  if (!empty($form_state['storage']) && $confirm_form = system_modules_uninstall_confirm_form($form_state['storage'])) {
     return $confirm_form;
   }
 
