diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4026,7 +4026,9 @@
     'PhabricatorProjectWorkboardBackgroundTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php',
     'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php',
     'PhabricatorProjectWorkboardTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardTransaction.php',
+    'PhabricatorProjectsAllPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsAllPolicyRule.php',
     'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php',
+    'PhabricatorProjectsBasePolicyRule' => 'applications/project/policyrule/PhabricatorProjectsBasePolicyRule.php',
     'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php',
     'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
     'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
@@ -9893,7 +9895,9 @@
     'PhabricatorProjectWorkboardBackgroundTransaction' => 'PhabricatorProjectTransactionType',
     'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem',
     'PhabricatorProjectWorkboardTransaction' => 'PhabricatorProjectTransactionType',
+    'PhabricatorProjectsAllPolicyRule' => 'PhabricatorProjectsBasePolicyRule',
     'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
+    'PhabricatorProjectsBasePolicyRule' => 'PhabricatorPolicyRule',
     'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
     'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
     'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
@@ -9902,7 +9906,7 @@
     'PhabricatorProjectsMailEngineExtension' => 'PhabricatorMailEngineExtension',
     'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
-    'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule',
+    'PhabricatorProjectsPolicyRule' => 'PhabricatorProjectsBasePolicyRule',
     'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
     'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
     'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
--- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
+++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php
@@ -1177,6 +1177,100 @@
     $this->assertTrue($can_edit);
   }
 
+  public function testProjectPolicyRules() {
+    $author = $this->generateNewTestUser();
+
+    $proj_a = PhabricatorProject::initializeNewProject($author)
+      ->setName('Policy A')
+      ->save();
+    $proj_b = PhabricatorProject::initializeNewProject($author)
+      ->setName('Policy B')
+      ->save();
+
+    $user_none = $this->generateNewTestUser();
+    $user_any = $this->generateNewTestUser();
+    $user_all = $this->generateNewTestUser();
+
+    $this->joinProject($proj_a, $user_any);
+    $this->joinProject($proj_a, $user_all);
+    $this->joinProject($proj_b, $user_all);
+
+    $any_policy = id(new PhabricatorPolicy())
+      ->setRules(
+        array(
+          array(
+            'action' => PhabricatorPolicy::ACTION_ALLOW,
+            'rule' => 'PhabricatorProjectsPolicyRule',
+            'value' => array(
+              $proj_a->getPHID(),
+              $proj_b->getPHID(),
+            ),
+          ),
+        ))
+      ->save();
+
+    $all_policy = id(new PhabricatorPolicy())
+      ->setRules(
+        array(
+          array(
+            'action' => PhabricatorPolicy::ACTION_ALLOW,
+            'rule' => 'PhabricatorProjectsAllPolicyRule',
+            'value' => array(
+              $proj_a->getPHID(),
+              $proj_b->getPHID(),
+            ),
+          ),
+        ))
+      ->save();
+
+    $any_task = ManiphestTask::initializeNewTask($author)
+      ->setViewPolicy($any_policy->getPHID())
+      ->save();
+
+    $all_task = ManiphestTask::initializeNewTask($author)
+      ->setViewPolicy($all_policy->getPHID())
+      ->save();
+
+    $map = array(
+      array(
+        pht('Project policy rule; user in no projects'),
+        $user_none,
+        false,
+        false,
+      ),
+      array(
+        pht('Project policy rule; user in some projects'),
+        $user_any,
+        true,
+        false,
+      ),
+      array(
+        pht('Project policy rule; user in all projects'),
+        $user_all,
+        true,
+        true,
+      ),
+    );
+
+    foreach ($map as $test_case) {
+      list($label, $user, $expect_any, $expect_all) = $test_case;
+
+      $can_any = PhabricatorPolicyFilter::hasCapability(
+        $user,
+        $any_task,
+        PhabricatorPolicyCapability::CAN_VIEW);
+
+      $can_all = PhabricatorPolicyFilter::hasCapability(
+        $user,
+        $all_task,
+        PhabricatorPolicyCapability::CAN_VIEW);
+
+      $this->assertEqual($expect_any, $can_any, pht('%s / Any', $label));
+      $this->assertEqual($expect_all, $can_all, pht('%s / All', $label));
+    }
+  }
+
+
   private function moveToColumn(
     PhabricatorUser $viewer,
     PhabricatorProject $board,
diff --git a/src/applications/project/policyrule/PhabricatorProjectsAllPolicyRule.php b/src/applications/project/policyrule/PhabricatorProjectsAllPolicyRule.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/policyrule/PhabricatorProjectsAllPolicyRule.php
@@ -0,0 +1,29 @@
+<?php
+
+final class PhabricatorProjectsAllPolicyRule
+  extends PhabricatorProjectsBasePolicyRule {
+
+  public function getRuleDescription() {
+    return pht('members of all projects');
+  }
+
+  public function applyRule(
+    PhabricatorUser $viewer,
+    $value,
+    PhabricatorPolicyInterface $object) {
+
+    $memberships = $this->getMemberships($viewer->getPHID());
+    foreach ($value as $project_phid) {
+      if (empty($memberships[$project_phid])) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  public function getRuleOrder() {
+    return 205;
+  }
+
+}
diff --git a/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php b/src/applications/project/policyrule/PhabricatorProjectsBasePolicyRule.php
copy from src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php
copy to src/applications/project/policyrule/PhabricatorProjectsBasePolicyRule.php
--- a/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php
+++ b/src/applications/project/policyrule/PhabricatorProjectsBasePolicyRule.php
@@ -1,12 +1,12 @@
 <?php
 
-final class PhabricatorProjectsPolicyRule
+abstract class PhabricatorProjectsBasePolicyRule
   extends PhabricatorPolicyRule {
 
   private $memberships = array();
 
-  public function getRuleDescription() {
-    return pht('members of projects');
+  protected function getMemberships($viewer_phid) {
+    return idx($this->memberships, $viewer_phid, array());
   }
 
   public function willApplyRules(
@@ -29,20 +29,6 @@
     }
   }
 
-  public function applyRule(
-    PhabricatorUser $viewer,
-    $value,
-    PhabricatorPolicyInterface $object) {
-
-    foreach ($value as $project_phid) {
-      if (isset($this->memberships[$viewer->getPHID()][$project_phid])) {
-        return true;
-      }
-    }
-
-    return false;
-  }
-
   public function getValueControlType() {
     return self::CONTROL_TYPE_TOKENIZER;
   }
@@ -57,10 +43,6 @@
     return $this->getDatasourceTemplate($datasource);
   }
 
-  public function getRuleOrder() {
-    return 200;
-  }
-
   public function getValueForStorage($value) {
     PhutilTypeSpec::newFromString('list<string>')->check($value);
     return array_values($value);
diff --git a/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php b/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php
--- a/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php
+++ b/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php
@@ -1,32 +1,10 @@
 <?php
 
 final class PhabricatorProjectsPolicyRule
-  extends PhabricatorPolicyRule {
-
-  private $memberships = array();
+  extends PhabricatorProjectsBasePolicyRule {
 
   public function getRuleDescription() {
-    return pht('members of projects');
-  }
-
-  public function willApplyRules(
-    PhabricatorUser $viewer,
-    array $values,
-    array $objects) {
-
-    $values = array_unique(array_filter(array_mergev($values)));
-    if (!$values) {
-      return;
-    }
-
-    $projects = id(new PhabricatorProjectQuery())
-      ->setViewer(PhabricatorUser::getOmnipotentUser())
-      ->withMemberPHIDs(array($viewer->getPHID()))
-      ->withPHIDs($values)
-      ->execute();
-    foreach ($projects as $project) {
-      $this->memberships[$viewer->getPHID()][$project->getPHID()] = true;
-    }
+    return pht('members of any project');
   }
 
   public function applyRule(
@@ -34,8 +12,9 @@
     $value,
     PhabricatorPolicyInterface $object) {
 
+    $memberships = $this->getMemberships($viewer->getPHID());
     foreach ($value as $project_phid) {
-      if (isset($this->memberships[$viewer->getPHID()][$project_phid])) {
+      if (isset($memberships[$project_phid])) {
         return true;
       }
     }
@@ -43,40 +22,8 @@
     return false;
   }
 
-  public function getValueControlType() {
-    return self::CONTROL_TYPE_TOKENIZER;
-  }
-
-  public function getValueControlTemplate() {
-    $datasource = id(new PhabricatorProjectDatasource())
-      ->setParameters(
-        array(
-          'policy' => 1,
-        ));
-
-    return $this->getDatasourceTemplate($datasource);
-  }
-
   public function getRuleOrder() {
     return 200;
   }
 
-  public function getValueForStorage($value) {
-    PhutilTypeSpec::newFromString('list<string>')->check($value);
-    return array_values($value);
-  }
-
-  public function getValueForDisplay(PhabricatorUser $viewer, $value) {
-    $handles = id(new PhabricatorHandleQuery())
-      ->setViewer($viewer)
-      ->withPHIDs($value)
-      ->execute();
-
-    return mpull($handles, 'getFullName', 'getPHID');
-  }
-
-  public function ruleHasEffect($value) {
-    return (bool)$value;
-  }
-
 }