Merge "Add support for names-factory provided by a plugins" into stable-3.5
diff --git a/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4 b/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4
index 3869643..dc46655 100644
--- a/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4
+++ b/src/main/antlr4/com/googlesource/gerrit/plugins/task/TaskReference.g4
@@ -98,7 +98,7 @@
   ;
 
 file_path
- : ALL_PROJECTS_ROOT
+ : ROOT_PROJECT
  | FWD_SLASH absolute TASK_DELIMETER
  | user absolute? TASK_DELIMETER
  | group_name absolute? TASK_DELIMETER
@@ -175,7 +175,7 @@
  : '^'
  ;
 
-ALL_PROJECTS_ROOT
+ROOT_PROJECT
  : FWD_SLASH FWD_SLASH TASK_DELIMETER
  ;
 
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigCache.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigCache.java
index 648c729..8f7d1f6 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigCache.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskConfigCache.java
@@ -17,7 +17,6 @@
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.entities.BranchNameKey;
 import com.google.gerrit.entities.Project;
-import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.config.AllProjectsName;
@@ -42,6 +41,7 @@
 
   protected final CurrentUser user;
   protected final AllProjectsName allProjects;
+  protected final TaskPluginConfiguration config;
 
   protected final Map<BranchNameKey, PatchSetArgument> psaMasquerades = new HashMap<>();
   protected final Map<FileKey, TaskConfig> taskCfgByFile = new HashMap<>();
@@ -51,25 +51,23 @@
       AllProjectsNameProvider allProjectsNameProvider,
       GitRepositoryManager gitMgr,
       PermissionBackend permissionBackend,
-      CurrentUser user) {
+      CurrentUser user,
+      TaskPluginConfiguration config) {
     this.allProjects = allProjectsNameProvider.get();
     this.gitMgr = gitMgr;
     this.permissionBackend = permissionBackend;
     this.user = user;
+    this.config = config;
   }
 
   public TaskConfig getRootConfig() throws ConfigInvalidException, IOException {
-    return getTaskConfig(FileKey.create(getRootBranch(), TaskFileConstants.TASK_CFG));
+    return getTaskConfig(FileKey.create(config.getRootConfigBranch(), TaskFileConstants.TASK_CFG));
   }
 
   public void masquerade(PatchSetArgument psa) {
     psaMasquerades.put(psa.change.getDest(), psa);
   }
 
-  protected BranchNameKey getRootBranch() {
-    return BranchNameKey.create(allProjects, RefNames.REFS_CONFIG);
-  }
-
   public TaskConfig getTaskConfig(FileKey key) throws ConfigInvalidException, IOException {
     TaskConfig cfg = taskCfgByFile.get(key);
     if (cfg == null) {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java
index bd0b683..609160e 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskKey.java
@@ -69,18 +69,21 @@
     protected String file;
     protected String task;
     protected GroupCache groupCache;
+    protected TaskPluginConfiguration config;
 
     Builder(
         FileKey relativeTo,
         AllProjectsName allProjectsName,
         AllUsersName allUsersName,
         AccountCache accountCache,
-        GroupCache groupCache) {
+        GroupCache groupCache,
+        TaskPluginConfiguration config) {
       this.relativeTo = relativeTo;
       this.allProjectsName = allProjectsName;
       this.allUsersName = allUsersName;
       this.accountCache = accountCache;
       this.groupCache = groupCache;
+      this.config = config;
     }
 
     public TaskKey buildTaskKey() {
@@ -167,8 +170,8 @@
                       .getGroupUUID()));
     }
 
-    public void setReferringAllProjectsTask() {
-      branch = BranchNameKey.create(allProjectsName, RefNames.REFS_CONFIG);
+    public void setReferringRootConfigBranchTask() {
+      branch = config.getRootConfigBranch();
     }
 
     protected void throwIfInvalidPath() throws ConfigInvalidException {
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskPluginConfiguration.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskPluginConfiguration.java
index 888c529..496d005 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskPluginConfiguration.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskPluginConfiguration.java
@@ -14,7 +14,14 @@
 
 package com.googlesource.gerrit.plugins.task;
 
+import static com.google.common.base.MoreObjects.firstNonNull;
+
+import com.google.gerrit.entities.BranchNameKey;
+import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
 import com.google.gerrit.extensions.annotations.PluginName;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.PluginConfigFactory;
 import com.google.inject.Inject;
@@ -31,28 +38,39 @@
   private static final String CACHEABLE_PREDICATES_KEY = "className";
   private static final String DEPRECATED_CACHEABLE_PREDICATES = "cacheable-predicates";
   private static final String DEPRECATED_CACHEABLE_PREDICATES_KEY = "byBranch-className";
-
-  private final Set<String> cacheableByBranchPredicateClassNames;
+  private static final String ROOT_CONFIG = "rootConfig";
+  private static final String ROOT_CONFIG_PROJECT_KEY = "project";
+  private static final String ROOT_CONFIG_BRANCH_KEY = "branch";
+  private final String plugin;
   private final Config gerritConfig;
   private final Config pluginConfig;
-  private final String plugin;
+  private final AllProjectsName allProjectsName;
+  private final Set<String> cacheableByBranchPredicateClassNames;
+  private final BranchNameKey rootConfigBranch;
 
   @Inject
   public TaskPluginConfiguration(
       @PluginName String plugin,
       @GerritServerConfig Config gerritConfig,
-      PluginConfigFactory pluginConfigFactory) {
+      PluginConfigFactory pluginConfigFactory,
+      AllProjectsNameProvider allProjectsNameProvider) {
     this.plugin = plugin;
     this.gerritConfig = gerritConfig;
     this.pluginConfig = pluginConfigFactory.getGlobalPluginConfig(plugin);
+    this.allProjectsName = allProjectsNameProvider.get();
     cacheableByBranchPredicateClassNames =
         new HashSet<>(Arrays.asList(readCacheableByBranchPredicateClassNames()));
+    rootConfigBranch = readRootConfigBranch();
   }
 
   public Set<String> getCacheableByBranchPredicateClassNames() {
     return cacheableByBranchPredicateClassNames;
   }
 
+  public BranchNameKey getRootConfigBranch() {
+    return rootConfigBranch;
+  }
+
   private String[] readCacheableByBranchPredicateClassNames() {
     String[] fromPluginConfig =
         pluginConfig.getStringList(
@@ -65,4 +83,12 @@
     return gerritConfig.getStringList(
         plugin, DEPRECATED_CACHEABLE_PREDICATES, DEPRECATED_CACHEABLE_PREDICATES_KEY);
   }
+
+  private BranchNameKey readRootConfigBranch() {
+    String project = pluginConfig.getString(ROOT_CONFIG, null, ROOT_CONFIG_PROJECT_KEY);
+    String branch = pluginConfig.getString(ROOT_CONFIG, null, ROOT_CONFIG_BRANCH_KEY);
+    return BranchNameKey.create(
+        project != null ? Project.NameKey.parse(project) : allProjectsName,
+        firstNonNull(branch, RefNames.REFS_CONFIG));
+  }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java
index a7824ac..1ccf673 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskReference.java
@@ -48,6 +48,7 @@
       AllUsersNameProvider allUsersNameProvider,
       AccountCache accountCache,
       GroupCache groupCache,
+      TaskPluginConfiguration config,
       @Assisted FileKey relativeTo,
       @Assisted String reference) {
     this(
@@ -56,7 +57,8 @@
             allProjectsNameProvider.get(),
             allUsersNameProvider.get(),
             accountCache,
-            groupCache),
+            groupCache,
+            config),
         reference);
   }
 
@@ -133,8 +135,8 @@
 
     @Override
     public void enterFile_path(TaskReferenceParser.File_pathContext ctx) {
-      if (ctx.ALL_PROJECTS_ROOT() != null || (ctx.FWD_SLASH() != null && ctx.absolute() != null)) {
-        builder.setReferringAllProjectsTask();
+      if (ctx.ROOT_PROJECT() != null || (ctx.FWD_SLASH() != null && ctx.absolute() != null)) {
+        builder.setReferringRootConfigBranchTask();
       }
 
       if (ctx.absolute() == null && ctx.relative() == null) {
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 7900ef7..5f13e18 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -23,3 +23,23 @@
 [cacheablePredicates "byBranch"]
         className = com.google.gerrit.server.query.change.BranchSetPredicate
 ```
+
+### Section "rootConfig"
+
+The rootConfig section can be used to configure the project and branch containing the root task.config.
+
+#### rootConfig.project
+
+The plugin will fetch the root task.config from the project set for this key. Defaults to `All-Projects`.
+
+#### rootConfig.branch
+
+The plugin will fetch the root task.config from the branch set for this key. Defaults to `refs/meta/config`.
+
+Example:
+
+```
+[rootConfig]
+        project = task/configuration
+        branch = refs/heads/master
+```
diff --git a/src/main/resources/Documentation/design/task_plugin_config.md b/src/main/resources/Documentation/design/task_plugin_config.md
new file mode 100644
index 0000000..edbe2a1
--- /dev/null
+++ b/src/main/resources/Documentation/design/task_plugin_config.md
@@ -0,0 +1,100 @@
+Task Plugin Config
+==================
+
+***
+
+**Requirements**
+----------------
+1. Task config devs should be able to collaboratively develop and test config using their own accounts
+    1. Must support collaboration across task config devs in various teams
+    2. Devs can preview task config changes before submitting them (using --preview)
+    3. Admins/devs can find the config where a specific task is defined (using --include-paths)
+2. Gerrit users should be able to view applicable tasks on their changes even when those tasks come from various team specific configs
+3. Task config devs should be able to configure tasks whose definition and applicability are visible only to a desired set of users
+    1. Users outside that set should not see invalid tasks if the tasks are not applicable on their changes
+4. Task config devs should be able to create automated jobs to
+    1. Verify the correctness of their task config changes
+    2. Merge task config updates after their desired gating criteria is met
+    3. Create/update task config changes
+    4. Only validate the roots they are interested in
+5. Task config devs can define gating criteria for task configs they own
+    1.  Gating criteria for task configs can be defined using task config (nice to have)
+    2.  Can block submit until dependent change (group query/destinations update for example) is submitted
+
+***
+
+The next two sections highlight how meeting some of the above-listed
+requirements is difficult with the default configuration and compares that
+configuration versus a custom root project and branch configuration.
+
+**Task config root in All-Projects:refs/meta/config (default)**
+---------------------------------------------------------------
+#### Cons:
+  1. refs/meta/config has restricted permissions usually, so admins have to get involved to get config updates merged. Hard to satisfy requirement [4.2](#requirements)
+  2. Permissions on refs/meta/config have to be relaxed so that task config devs can read/push on that ref which may expose other configs on that ref
+  3. Having config in All-Projects leads to customized labels, submit requirements and code-owners config being inherited by other projects we didn't intend to apply to
+  4. Requires special setup for fetching/pushing to refs/meta/config
+  5. Relies on group refs for requirements [3, 4 ,5](#requirements). See [Solutions for requirement [3]](#solutions-for-requirement-3)
+#### Pros:
+  1. Consistent with other plugin configs
+  2. Common location for task config devs to share/find task config
+  3. Admins control task roots
+
+
+**Task config root in a custom project:branch**
+-----------------------------------------------
+#### Cons:
+  1. Not consistent with other plugin configs
+  2. Location of task config might be hard to find by task config devs
+  3. Relies on group refs for requirement [3](#requirements). See [Solutions for requirement [3]](#solutions-for-requirement-3)
+  4. Admins potentially have less oversight on task roots
+  5. Cannot have project configuration such as labels, submit requirements, code-owners per team. Independent project:branch per team needs to be used if this is needed.
+#### Pros:
+  1. Admins do only one time setup for project permissions
+  2. Permissions on the ref containing task configs can be setup as desired by the custom project:branch owner (might not include all task config devs)
+  3. Can customize labels, create new submit requirements and enable other plugins(code-owners for example) without any impact on other projects
+  4. Reduces dependency on moving team config to independent projects/branches
+
+
+***
+
+This section details the known solutions to requirement [3] whilst highlighting advantages and limitations of each solution.
+
+**Solutions for requirement [3]**
+---------------------------------
+### **Team task config on group refs**
+#### Cons:
+  1. Group refs are only visible to group owners and upstream does not want to change this. Service accounts and collaborators would need to become group owners in order to do anything useful with the team's group-based task config.
+  2. Requires internal Gerrit group
+  3. Requires special setup for fetching/pushing to refs/groups/..
+#### Pros:
+  1. Admins do a one-time setup to allow everyone to push/review/submit to group refs
+  2. Gerrit groups already have a concept of visibility based on membership
+
+### **Team task config on custom group refs (refs/heads/group-owned/..) in All-Users**
+#### Cons:
+  1. Not consistent with other plugin configs
+  2. Location of task config might be hard to find by task config devs
+  3. Admins have to set up permissions on the new refs/heads/group-owned/.. ref. See implementation gaps.
+  4. There is a risk that any project customization can interfere with core's use of All-Users
+  5. These refs are not an existing gerrit concept and might be hard to get acceptance from upstream
+#### Pros:
+  1. Permissions on the ref containing task configs can be setup as desired by the task config devs
+  2. Can customize labels, create new submit requirements and enable other plugins(code-owners for example) without any impact on other projects
+  3. Gerrit groups already have a concept of visibility based on membership
+#### Implementation gap:
+  1. Plugin currently does not support task expressions referring to a refs/heads/group-owned/..
+  2. No support for automatically configuring permissions on refs/heads/group-owned/.. or otherwise tying the ref to the group
+
+### **Team task config on custom project branch**
+#### Cons:
+  1. Not consistent with other plugin configs (but might be similar if refs/meta/config is used as the branch)
+  2. Location of task config might be hard to find by task config devs
+  3. Admins have to setup new projects for each team if existing projects are not used
+#### Pros:
+  1. Admins do only one time setup for project permissions. Might already be done if it is an existing project.
+  2. Permissions on the ref containing task configs can be setup as desired by the task config devs
+  3. Can customize labels, create new submit requirements and enable other plugins(code-owners for example) without any impact on other projects
+#### Implementation gap:
+  1. Plugin currently does not support task expressions referring to a custom project:branch
+
diff --git a/src/main/resources/Documentation/task.md b/src/main/resources/Documentation/task.md
index 4ee32b2..4cf8c71 100644
--- a/src/main/resources/Documentation/task.md
+++ b/src/main/resources/Documentation/task.md
@@ -72,12 +72,13 @@
 
 Tasks
 -----
-Tasks can either be root tasks, or subtasks. Tasks are defined in the
-`All-Projects` project, on the `refs/meta/config` branch, in a file named
-`task.config`. This file uses the gitconfig format to define tasks. The
-special "True" keyword may be used as any query definition to indicate
-an always matching query. The following keys may be defined in any
-task section:
+Tasks can either be root tasks, or subtasks. Tasks are expected to be
+defined in the `All-Projects` project, on the `refs/meta/config` branch
+(the root project and branch are [configurable](config.html#section-rootconfig)),
+in a file named `task.config`. This file uses the gitconfig format to define
+tasks. The special "True" keyword may be used as any query definition
+to indicate an always matching query. The following keys may be defined
+in any task section:
 
 `applicable`
 
diff --git a/src/main/resources/Documentation/task_expression.md b/src/main/resources/Documentation/task_expression.md
index c084a39..61f46d0 100644
--- a/src/main/resources/Documentation/task_expression.md
+++ b/src/main/resources/Documentation/task_expression.md
@@ -154,10 +154,12 @@
     ...
 ```
 
-To reference a task from root task.config on the All-Projects.git, prefix the task name with `//^`
-and to reference a task from task dir on the All-Projects.git, use
-`//<relative path from task dir>^<task_name>`. It doesn't matter which project, ref and file one
-is referencing from while using this syntax.
+To reference a task from the root task.config on the root project and branch
+(defaults to `All-Projects` and `refs/meta/config` and is
+[configurable](config.html#section-rootconfig)), prefix the task name with
+`//^` and to reference a task from the task dir on the root project and branch,
+use `//<relative path from task dir>^<task_name>`. It doesn't matter which
+project, ref and file one is referencing from while using this syntax.
 
 Example:
 
diff --git a/src/main/resources/Documentation/test/paths.md b/src/main/resources/Documentation/test/paths.md
index 8f43cb0..54dca08 100644
--- a/src/main/resources/Documentation/test/paths.md
+++ b/src/main/resources/Documentation/test/paths.md
@@ -1,5 +1,7 @@
-`task.config` file in project `All-Projects` on ref `refs/meta/config`.
+The config below is expected to be in the `task.config` file in project
+`{root-cfg-prj}` on ref `{root-cfg-branch}`.
 
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
 [root "Root Task PATHS"]
   subtask = subtask pass
@@ -13,10 +15,10 @@
    "hasPass" : false,
    "name" : "Root Task PATHS",
    "path" : {
-      "ref" : "refs/meta/config",
+      "ref" : "{root-cfg-branch}",
       "file" : "task.config",
       "name" : "Root Task PATHS",
-      "project" : "All-Projects",
+      "project" : "{root-cfg-prj}",
       "type" : "root"
    },
    "status" : "PASS",
@@ -26,10 +28,10 @@
          "hasPass" : true,
          "name" : "subtask pass",
          "path" : {
-            "ref" : "refs/meta/config",
+            "ref" : "{root-cfg-branch}",
             "file" : "task.config",
             "name" : "subtask pass",
-            "project" : "All-Projects",
+            "project" : "{root-cfg-prj}",
             "type" : "task"
          },
          "status" : "PASS"
@@ -46,10 +48,10 @@
    "hasPass" : false,
    "name" : "Root other FILE",
    "path" : {
-      "ref" : "refs/meta/config",
+      "ref" : "{root-cfg-branch}",
       "file" : "task.config",
       "name" : "Root other FILE",
-      "project" : "All-Projects",
+      "project" : "{root-cfg-prj}",
       "type" : "root"
    },
    "status" : "WAITING",
@@ -59,10 +61,10 @@
          "hasPass" : true,
          "name" : "file task/common.config PASS",
          "path" : {
-            "ref" : "refs/meta/config",
+            "ref" : "{root-cfg-branch}",
             "file" : "task/common.config",
             "name" : "file task/common.config PASS",
-            "project" : "All-Projects",
+            "project" : "{root-cfg-prj}",
             "type" : "task"
          },
          "status" : "PASS"
@@ -72,10 +74,10 @@
          "hasPass" : true,
          "name" : "file task/common.config FAIL",
          "path" : {
-            "ref" : "refs/meta/config",
+            "ref" : "{root-cfg-branch}",
             "file" : "task/common.config",
             "name" : "file task/common.config FAIL",
-            "project" : "All-Projects",
+            "project" : "{root-cfg-prj}",
             "type" : "task"
          },
          "status" : "FAIL"
@@ -99,10 +101,10 @@
    "hasPass" : false,
    "name" : "Root tasks-factory",
    "path" : {
-      "ref" : "refs/meta/config",
+      "ref" : "{root-cfg-branch}",
       "file" : "task.config",
       "name" : "Root tasks-factory",
-      "project" : "All-Projects",
+      "project" : "{root-cfg-prj}",
       "type" : "root"
    },
    "status" : "WAITING",
@@ -112,10 +114,10 @@
          "hasPass" : false,
          "name" : "my a task",
          "path" : {
-            "ref" : "refs/meta/config",
+            "ref" : "{root-cfg-branch}",
             "file" : "task.config",
             "name" : "my a task",
-            "project" : "All-Projects",
+            "project" : "{root-cfg-prj}",
             "tasksFactory" : "tasks-factory example",
             "type" : "tasks-factory"
          },
@@ -126,10 +128,10 @@
          "hasPass" : false,
          "name" : "my b task",
          "path" : {
-            "ref" : "refs/meta/config",
+            "ref" : "{root-cfg-branch}",
             "file" : "task.config",
             "name" : "my b task",
-            "project" : "All-Projects",
+            "project" : "{root-cfg-prj}",
             "tasksFactory" : "tasks-factory example",
             "type" : "tasks-factory"
          },
@@ -150,10 +152,10 @@
    "hasPass" : false,
    "name" : "Root other PROJECT",
    "path" : {
-      "ref" : "refs/meta/config",
+      "ref" : "{root-cfg-branch}",
       "file" : "task.config",
       "name" : "Root other PROJECT",
-      "project" : "All-Projects",
+      "project" : "{root-cfg-prj}",
       "type" : "root"
    },
    "status" : "WAITING",
@@ -189,8 +191,11 @@
    ]
 }
 ```
-`task.config` file in project `All-Projects` on ref `refs/meta/config`.
 
+The config below is expected to be in the `task.config` file in project
+`{root-cfg-prj}` on ref `{root-cfg-branch}`.
+
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
 [root "Root Capability Error"]
     applicable = is:open
diff --git a/src/main/resources/Documentation/test/task-preview/new_root_with_original_with_external_secret_ref.md b/src/main/resources/Documentation/test/task-preview/new_root_with_original_with_external_secret_ref.md
index 3d1d7cc..950d87c 100644
--- a/src/main/resources/Documentation/test/task-preview/new_root_with_original_with_external_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/new_root_with_original_with_external_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview a new root, original root with subtasks-external pointing to secret user ref.
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
  [root "Root with SECRET external"]
      applicable = is:open
diff --git a/src/main/resources/Documentation/test/task-preview/non-secret_ref_with_external_secret_ref.md b/src/main/resources/Documentation/test/task-preview/non-secret_ref_with_external_secret_ref.md
index d192050..b5f04d7 100644
--- a/src/main/resources/Documentation/test/task-preview/non-secret_ref_with_external_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/non-secret_ref_with_external_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview a non-secret user ref with subtasks-external pointing to secret user ref.
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
 [root "Root for NON-SECRET external Preview with SECRET external"]
     applicable = "is:open"
diff --git a/src/main/resources/Documentation/test/task-preview/non_root_with_subtask_from_root_task.md b/src/main/resources/Documentation/test/task-preview/non_root_with_subtask_from_root_task.md
index a0f11eb..afb80b0 100644
--- a/src/main/resources/Documentation/test/task-preview/non_root_with_subtask_from_root_task.md
+++ b/src/main/resources/Documentation/test/task-preview/non_root_with_subtask_from_root_task.md
@@ -1,6 +1,6 @@
 # --task-preview non-root file with subtask pointing root task
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
 [root "Points to subFile task with rootFile task preview"]
     applicable = is:open
@@ -12,7 +12,7 @@
     pass = True
 ```
 
-file: `All-Projects.git:refs/meta/config:task/foo/bar/baz.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task/foo/bar/baz.config`
 ```
  [task "Preview pointing to rootFile task"]
      applicable = is:open
diff --git a/src/main/resources/Documentation/test/task-preview/root_with_external_non-secret_ref_with_external_secret_ref.md b/src/main/resources/Documentation/test/task-preview/root_with_external_non-secret_ref_with_external_secret_ref.md
index c0add08..45a069d 100644
--- a/src/main/resources/Documentation/test/task-preview/root_with_external_non-secret_ref_with_external_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/root_with_external_non-secret_ref_with_external_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview root file with subtasks-external pointing to a non-secret user ref with subtasks-external pointing to a secret user ref.
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
  [root "Root Preview NON-SECRET external with SECRET external"]
      applicable = "is:open"
diff --git a/src/main/resources/Documentation/test/task-preview/root_with_external_secret_ref.md b/src/main/resources/Documentation/test/task-preview/root_with_external_secret_ref.md
index 4d81b12..d41bc8b 100644
--- a/src/main/resources/Documentation/test/task-preview/root_with_external_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/root_with_external_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview root file with subtasks-external pointing to secret user ref
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
  [root "Root Preview SECRET external"]
      applicable = is:open
diff --git a/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md b/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md
index 504d5a8..883dc0d 100644
--- a/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview root file with subtask pointing to a non-secret group ref with subtask pointing to a secret group ref.
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
  [root "Root Preview NON-SECRET group subtask with SECRET group subtask"]
      applicable = "is:open"
diff --git a/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_secret_ref.md b/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_secret_ref.md
index 9a1932c..6a879cb 100644
--- a/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/subtask_using_group_syntax/root_with_subtask_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview root file with subtask pointing to secret group ref
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
  [root "Root Preview SECRET external group"]
      applicable = is:open
diff --git a/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md
index fad2f1d..a9469eb 100644
--- a/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_non-secret_ref_with_subtask_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview root file with subtask pointing to a non-secret user ref with subtask pointing to a secret user ref.
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
  [root "Root Preview NON-SECRET subtask with SECRET subtask"]
      applicable = "is:open"
diff --git a/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_secret_ref.md b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_secret_ref.md
index d2b3349..007f53a 100644
--- a/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_secret_ref.md
+++ b/src/main/resources/Documentation/test/task-preview/subtask_using_user_syntax/root_with_subtask_secret_ref.md
@@ -1,6 +1,6 @@
 # --task-preview root file with subtask pointing to secret user ref
 
-file: `All-Projects.git:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
  [root "Root Preview SECRET external"]
      applicable = is:open
diff --git a/src/main/resources/Documentation/test/task_states.md b/src/main/resources/Documentation/test/task_states.md
index d955da0..54ef4fb 100644
--- a/src/main/resources/Documentation/test/task_states.md
+++ b/src/main/resources/Documentation/test/task_states.md
@@ -15,9 +15,9 @@
 ```
 
 The config below is expected to be in the `task.config` file in project
-`All-Projects` on ref `refs/meta/config`.
+`{root-cfg-prj}` on ref `{root-cfg-branch}`.
 
-file: `All-Projects:refs/meta/config:task.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task.config`
 ```
 [root "Root N/A"]
   applicable = is:closed # Assumes test query is "is:open"
@@ -2438,18 +2438,18 @@
    ]
 }
 
-[root "Root Reference tasks from All-Projects"]
+[root "Root Reference tasks from {root-cfg-prj}"]
   applicable = is:open
   subtask = //^Subtask PASS
-  subtask = @testuser/dir/relative.config^Import All-Projects root task
-  subtask = @testuser/dir/relative.config^Import All-Projects non-root task
-  subtask = %{non_secret_group_name_without_space}/dir/relative.config^Import All-Projects root task - groups
-  subtask = %{non_secret_group_name_without_space}/dir/relative.config^Import All-Projects non-root task - groups
+  subtask = @testuser/dir/relative.config^Import {root-cfg-prj} root task
+  subtask = @testuser/dir/relative.config^Import {root-cfg-prj} non-root task
+  subtask = %{non_secret_group_name_without_space}/dir/relative.config^Import {root-cfg-prj} root task - groups
+  subtask = %{non_secret_group_name_without_space}/dir/relative.config^Import {root-cfg-prj} non-root task - groups
 
 {
    "applicable" : true,
    "hasPass" : false,
-   "name" : "Root Reference tasks from All-Projects",
+   "name" : "Root Reference tasks from {root-cfg-prj}",
    "status" : "PASS",
    "subTasks" : [
       {
@@ -2461,7 +2461,7 @@
       {
          "applicable" : true,
          "hasPass" : false,
-         "name" : "Import All-Projects root task",
+         "name" : "Import {root-cfg-prj} root task",
          "status" : "PASS",
          "subTasks" : [
             {
@@ -2475,7 +2475,7 @@
       {
          "applicable" : true,
          "hasPass" : false,
-         "name" : "Import All-Projects non-root task",
+         "name" : "Import {root-cfg-prj} non-root task",
          "status" : "PASS",
          "subTasks" : [
             {
@@ -2489,7 +2489,7 @@
       {
          "applicable" : true,
          "hasPass" : false,
-         "name" : "Import All-Projects root task - groups",
+         "name" : "Import {root-cfg-prj} root task - groups",
          "status" : "PASS",
          "subTasks" : [
             {
@@ -2503,7 +2503,7 @@
       {
          "applicable" : true,
          "hasPass" : false,
-         "name" : "Import All-Projects non-root task - groups",
+         "name" : "Import {root-cfg-prj} non-root task - groups",
          "status" : "PASS",
          "subTasks" : [
             {
@@ -2740,7 +2740,7 @@
 }
 
 [root "Root Preload from user ref which has subtask in different file"]
-  preload-task = @testuser/dir/relative.config^Import All-Projects root task
+  preload-task = @testuser/dir/relative.config^Import {root-cfg-prj} root task
 
 {
    "applicable" : true,
@@ -3400,7 +3400,7 @@
 
 ```
 
-file: `All-Projects:refs/meta/config:task/common.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task/common.config`
 ```
 [task "file task/common.config PASS"]
   applicable = is:open
@@ -3411,7 +3411,7 @@
   fail = is:open
 ```
 
-file: `All-Projects:refs/meta/config:task/relative.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task/relative.config`
 ```
 [task "Root Import task from subdir using relative syntax"]
     subtask = dir/common.config^Sample relative task in sub dir
@@ -3421,7 +3421,7 @@
     pass = True
 ```
 
-file: `All-Projects:refs/meta/config:task/dir/common.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task/dir/common.config`
 ```
 [task "Sample relative task in sub dir"]
     applicable = is:open
@@ -3488,7 +3488,7 @@
     preload-task = %{non_secret_group_name_without_space}/foo/bar.config^Absolute Task 1
 ```
 
-file: `All-Projects:refs/meta/config:task/invalids.config`
+file: `{root-cfg-prj}:{root-cfg-branch}:task/invalids.config`
 ```
 [task "No PASS criteria"]
   fail-hint = Invalid without Pass criteria and without subtasks
@@ -3707,11 +3707,11 @@
   applicable = is:open
   pass = True
 
-[task "Import All-Projects root task"]
+[task "Import {root-cfg-prj} root task"]
   applicable = is:open
   subtask = //^Subtask PASS
 
-[task "Import All-Projects non-root task"]
+[task "Import {root-cfg-prj} non-root task"]
   applicable = is:open
   subtask = //dir/common.config^Sample relative task in sub dir
 ```
@@ -3732,11 +3732,11 @@
 
 file: `All-Users:refs/groups/{sharded_non_secret_group_uuid_without_space}:task/dir/relative.config`
 ```
-[task "Import All-Projects root task - groups"]
+[task "Import {root-cfg-prj} root task - groups"]
   applicable = is:open
   subtask = //^Subtask PASS
 
-[task "Import All-Projects non-root task - groups"]
+[task "Import {root-cfg-prj} non-root task - groups"]
   applicable = is:open
   subtask = //dir/common.config^Sample relative task in sub dir
 ```
diff --git a/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java b/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
index 8ea0009..7afbeab 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskExpressionTest.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
+import com.google.inject.Inject;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
 import junit.framework.TestCase;
@@ -54,6 +55,8 @@
   public static String REL_WORLD_PEACE_CFG = "world/peace.config";
   public static String ABS_PEACE_CFG = "/peace.config";
 
+  @Inject private TaskPluginConfiguration config;
+
   public void testBlank() {
     TaskExpression exp = getTaskExpression("");
     Iterator<TaskKey> it = exp.iterator();
@@ -245,7 +248,8 @@
                         new AllProjectsName("All-Projects"),
                         new AllUsersName("All-Users"),
                         accountCache,
-                        groupCache),
+                        groupCache,
+                        config),
                     (String) invocation.getArguments()[1]));
     return new TaskExpression(factory, file, expression);
   }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java b/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java
index e08240d..dc97644 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/task/TaskReferenceTest.java
@@ -25,16 +25,20 @@
 import com.google.gerrit.server.account.AccountState;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.config.AllUsersName;
+import com.google.gerrit.server.config.PluginConfigFactory;
 import java.sql.Timestamp;
 import java.util.NoSuchElementException;
 import java.util.Optional;
 import junit.framework.TestCase;
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
 import org.junit.Test;
 import org.mockito.Mockito;
 
 public class TaskReferenceTest extends TestCase {
+  private static final String PLUGIN = "task";
   private static final String ALL_USERS = "All-Users";
   private static final String ALL_PROJECTS = "All-Projects";
   public static String SIMPLE = "simple";
@@ -251,6 +255,8 @@
   protected static TaskKey getTaskFromReference(FileKey file, String expression) {
     AccountCache accountCache = Mockito.mock(AccountCache.class);
     GroupCache groupCache = Mockito.mock(GroupCache.class);
+    PluginConfigFactory pluginConfigFactory = Mockito.mock(PluginConfigFactory.class);
+    AllProjectsNameProvider allProjectsNameProvider = Mockito.mock(AllProjectsNameProvider.class);
     Mockito.when(accountCache.getByUsername(TEST_USER))
         .thenReturn(Optional.of(AccountState.forAccount(TEST_USER_ACCOUNT)));
     Mockito.when(groupCache.get(TEST_GROUP1_NAME)).thenReturn(Optional.of(TEST_GROUP1));
@@ -259,7 +265,8 @@
         .thenReturn(Optional.of(TEST_GROUP1));
     Mockito.when(groupCache.get(AccountGroup.uuid(TEST_GROUP2_UUID)))
         .thenReturn(Optional.of(TEST_GROUP2));
-
+    Mockito.when(allProjectsNameProvider.get()).thenReturn(new AllProjectsName(ALL_PROJECTS));
+    Mockito.when(pluginConfigFactory.getGlobalPluginConfig(PLUGIN)).thenReturn(new Config());
     try {
       return new TaskReference(
               new TaskKey.Builder(
@@ -267,7 +274,9 @@
                   new AllProjectsName(ALL_PROJECTS),
                   new AllUsersName(ALL_USERS),
                   accountCache,
-                  groupCache),
+                  groupCache,
+                  new TaskPluginConfiguration(
+                      PLUGIN, new Config(), pluginConfigFactory, allProjectsNameProvider)),
               expression)
           .getTaskKey();
     } catch (ConfigInvalidException e) {
diff --git a/test/check_task_statuses.sh b/test/check_task_statuses.sh
index dfb0e52..9a5735b 100755
--- a/test/check_task_statuses.sh
+++ b/test/check_task_statuses.sh
@@ -72,6 +72,8 @@
 
     --help|-h                         help text
     --server|-s                       gerrit host
+    --root-config-project             project containing the root task config
+    --root-config-branch              branch containing the root task config
     --non-secret-user                 user who don't have permission
                                       to view other user refs.
     --untrusted-user                  user who doesn't have permission
@@ -94,23 +96,14 @@
 DOCS=$MYDIR/.././src/main/resources/Documentation/test
 OUT=$MYDIR/../target/tests
 
-ALL=$OUT/All-Projects
-ALL_TASKS=$ALL/task
-
-USERS=$OUT/All-Users
-USER_TASKS=$USERS/task
-
-EXPECTED=$OUT/expected
-ACTUAL=$OUT/actual
-
-ROOT_CFG=$ALL/task.config
-
 # --- Args ----
 
 while (( "$#" )) ; do
     case "$1" in
         --help|-h)                        usage ;;
         --server|-s)                      shift ; SERVER=$1 ;;
+        --root-config-project)            shift ; ROOT_CONFIG_PRJ=$1 ;;
+        --root-config-branch)             shift ; ROOT_CONFIG_BRANCH=$1 ;;
         --non-secret-user)                shift ; NON_SECRET_USER=$1 ;;
         --untrusted-user)                 shift ; UNTRUSTED_USER=$1 ;;
         --non-secret-group-without-space) shift ; GROUP_NAME_WITHOUT_SPACE=$1 ;;
@@ -126,19 +119,31 @@
 [ -z "$GROUP_NAME_WITHOUT_SPACE" ] && usage "You must specify --non-secret-group-without-space"
 [ -z "$GROUP_NAME_WITH_SPACE" ] && usage "You must specify --non-secret-group-with-space"
 [ -z "$GERRIT_GIT_DIR" ] && usage "GERRIT_GIT_DIR environment variable not set"
+[ -z "$ROOT_CONFIG_PRJ" ] && ROOT_CONFIG_PRJ=All-Projects
+[ -z "$ROOT_CONFIG_BRANCH" ] && ROOT_CONFIG_BRANCH=refs/meta/config
 
 
 PORT=29418
 HTTP_PORT=8080
 PROJECT=test
 BRANCH=master
-REMOTE_ALL=ssh://$SERVER:$PORT/All-Projects
+REMOTE_ALL=ssh://$SERVER:$PORT/$ROOT_CONFIG_PRJ
 REMOTE_USERS=ssh://$SERVER:$PORT/All-Users
 REMOTE_TEST=ssh://$SERVER:$PORT/$PROJECT
-
-REF_ALL=refs/meta/config
+REF_ALL=$ROOT_CONFIG_BRANCH
 REF_USERS=refs/users/self
 
+ALL=$OUT/$ROOT_CONFIG_PRJ
+ALL_TASKS=$ALL/task
+
+USERS=$OUT/All-Users
+USER_TASKS=$USERS/task
+
+EXPECTED=$OUT/expected
+ACTUAL=$OUT/actual
+
+ROOT_CFG=$ALL/task.config
+
 CONFIG=$ROOT_CFG
 
 declare -A USER_REFS
diff --git a/test/check_task_visibility.sh b/test/check_task_visibility.sh
index 5e08811..15566f7 100755
--- a/test/check_task_visibility.sh
+++ b/test/check_task_visibility.sh
@@ -173,6 +173,8 @@
 
     --help|-h                     help text
     --server|-s                   gerrit host
+    --root-config-project         project containing the root task config
+    --root-config-branch          branch containing the root task config
     --non-secret-user             user who doesn't have permission
                                   to view other user refs.
     --non-secret-group            non-secret group name
@@ -187,6 +189,8 @@
     case "$1" in
         --help|-h)                usage ;;
         --server|-s)              shift ; SERVER=$1 ;;
+        --root-config-project)    shift ; ROOT_CONFIG_PRJ=$1 ;;
+        --root-config-branch)     shift ; ROOT_CONFIG_BRANCH=$1 ;;
         --non-secret-user)        shift ; NON_SECRET_USER=$1 ;;
         --non-secret-group)       shift ; NON_SECRET_GROUP_NAME=$1 ;;
         --secret-group)           shift ; SECRET_GROUP_NAME=$1 ;;
@@ -200,6 +204,8 @@
 [ -z "$NON_SECRET_GROUP_NAME" ] && usage "You must specify --non-secret-group"
 [ -z "$SECRET_GROUP_NAME" ] && usage "You must specify --secret-group"
 [ -z "$GERRIT_GIT_DIR" ] && usage "GERRIT_GIT_DIR environment variable not set"
+[ -z "$ROOT_CONFIG_PRJ" ] && ROOT_CONFIG_PRJ=All-Projects
+[ -z "$ROOT_CONFIG_BRANCH" ] && ROOT_CONFIG_BRANCH=refs/meta/config
 
 RESULT=0
 PORT=29418
@@ -240,7 +246,8 @@
 )
 
 for test in "${TESTS[@]}" ; do
-    TEST_DOC="$(replace_user_refs < "$TEST_DOC_DIR/$test" | replace_users | replace_groups)"
+    TEST_DOC="$(replace_user_refs < "$TEST_DOC_DIR/$test" | replace_users | replace_groups | \
+        replace_root_configs)"
     init_configs
     test_change
 done
diff --git a/test/docker/run.sh b/test/docker/run.sh
index 8ddd311..0335cfa 100755
--- a/test/docker/run.sh
+++ b/test/docker/run.sh
@@ -61,14 +61,44 @@
 }
 
 run_task_plugin_tests() {
+    echo "Running test suite with default root_project and root_branch"
     docker-compose "${COMPOSE_ARGS[@]}" up --detach
     docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=admin run_tests \
         '/task/test/docker/run_tests/start.sh'
+    # TODO: Once the 'retest' functionality is fixed, the re-run with custom root prj and
+    #       branch can be done without needing to bring down and start the containers again.
+    docker-compose "${COMPOSE_ARGS[@]}" down -v --rmi local 2>/dev/null
+
+    ROOT_CFG_PRJ=task-config
+    ROOT_CFG_BRANCH=refs/heads/master
+    echo "Re-running test suite with root_project=$ROOT_CFG_PRJ and root_branch=$ROOT_CFG_BRANCH"
+    docker-compose "${COMPOSE_ARGS[@]}" up --detach
+    configure_root_prj_and_branch
+    docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=admin run_tests \
+        sh -c "/task/test/docker/run_tests/start.sh \
+            --root-config-project $ROOT_CFG_PRJ \
+            --root-config-branch $ROOT_CFG_BRANCH"
+}
+
+configure_root_prj_and_branch() {
+    docker-compose "${COMPOSE_ARGS[@]}" exec -T gerrit-01 \
+        sh -c "git config -f \$GERRIT_SITE/etc/task.config rootConfig.project $ROOT_CFG_PRJ"
+    docker-compose "${COMPOSE_ARGS[@]}" exec -T gerrit-01 \
+        sh -c "git config -f \$GERRIT_SITE/etc/task.config rootConfig.branch $ROOT_CFG_BRANCH"
+    docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=admin run_tests \
+        sh -c "./task/test/docker/run_tests/wait-for-it.sh \$GERRIT_HOST:29418 -t -60" || \
+            die "Failed to start gerrit"
+    docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=admin run_tests \
+        sh -c "cat \$USER_HOME/.ssh/id_rsa.pub | ssh -p 29418 -i /server-ssh-key/ssh_host_rsa_key \
+            \"Gerrit Code Review@\$GERRIT_HOST\" suexec --as admin@example.com -- gerrit set-account \
+            admin --add-ssh-key -" || die "Failed to add ssh key to admin account"
+    docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=admin run_tests \
+        sh -c "ssh -p 29418 \$GERRIT_HOST gerrit plugin reload task" || die "Failed to reload plugin"
 }
 
 retest() {
-    docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=admin \
-        run_tests task/test/docker/run_tests/start.sh retest
+    docker-compose "${COMPOSE_ARGS[@]}" exec -T --user=admin run_tests \
+        sh -c "/task/test/docker/run_tests/start.sh --retest"
     RESULT=$?
     cleanup
 }
diff --git a/test/docker/run_tests/create-one-time-test-data.sh b/test/docker/run_tests/create-one-time-test-data.sh
index d949bb2..c0d9bd8 100755
--- a/test/docker/run_tests/create-one-time-test-data.sh
+++ b/test/docker/run_tests/create-one-time-test-data.sh
@@ -14,7 +14,7 @@
     gssh create-account "$UNTRUSTED_USER" --full-name "$UNTRUSTED_USER" \
         --email "$UNTRUSTED_USER"@example.com --ssh-key - < ~/.ssh/id_rsa.pub
 
-    gssh create-group "Visible-All-Projects-Config" --member "$NON_SECRET_USER"
+    gssh create-group "Visible-Root-Config-Project" --member "$NON_SECRET_USER"
 
     local secret_user=$USER
     gssh create-group "$NON_SECRET_GROUP_NAME_WITHOUT_SPACE" \
@@ -24,17 +24,40 @@
     gssh create-group "$SECRET_GROUP_NAME" --member "$secret_user"
 }
 
-setup_all_projects_repo() {
-    echo "Updating All-Projects repo ..."
+setup_root_cfg_repo() {
+    echo "Updating root-cfg repo ..."
 
-    local uuid=$(gssh ls-groups -v | awk '-F\t' '$1 == "Visible-All-Projects-Config" {print $2}')
+    local uuid=$(gssh ls-groups -v | awk '-F\t' '$1 == "Visible-Root-Config-Project" {print $2}')
     ( cd "$WORKSPACE"
-      q git clone ssh://"$GERRIT_HOST":"$SSH_PORT"/All-Projects allProjects
-      cd allProjects
+      if ! gssh ls-projects | grep -q "^$ROOT_CONFIG_PRJ$"; then
+          echo "Project $ROOT_CONFIG_PRJ does not exist. Creating ..."
+          gssh create-project "$ROOT_CONFIG_PRJ" --owner "Administrators" \
+              --submit-type "MERGE_IF_NECESSARY" --empty-commit
+      fi
+      if ! gssh ls-projects --show-branch "$ROOT_CONFIG_BRANCH" | grep -q "$ROOT_CONFIG_PRJ$"; then
+          echo "Branch $ROOT_CONFIG_BRANCH in $ROOT_CONFIG_PRJ does not exist. Creating ..."
+          gssh create-branch "$ROOT_CONFIG_PRJ" "$ROOT_CONFIG_BRANCH" master
+      fi
+      q git clone ssh://"$GERRIT_HOST":"$SSH_PORT"/"$ROOT_CONFIG_PRJ" root_cfg_prj
+      cd root_cfg_prj
       q git fetch origin refs/meta/config ; q git checkout FETCH_HEAD
-      echo -e "$uuid\tVisible-All-Projects-Config" >> groups
+
+      echo -e "$uuid\tVisible-Root-Config-Project" >> groups
       git config -f "project.config" \
-          --add access."refs/meta/config".read "group Visible-All-Projects-Config"
+          --add access."$ROOT_CONFIG_BRANCH".read "group Visible-Root-Config-Project"
+      git config -f "project.config" \
+          --add access."$ROOT_CONFIG_BRANCH".exclusiveGroupPermissions "read"
+      q git add . && q git commit -m "project config update"
+      q git push origin HEAD:refs/meta/config
+    )
+}
+
+setup_capabilities() {
+    echo "Updating All-Projects repo with capabilities ..."
+    ( cd "$WORKSPACE"
+      q git clone ssh://"$GERRIT_HOST":"$SSH_PORT"/All-Projects allProjects_capabilities
+      cd allProjects_capabilities
+      q git fetch origin refs/meta/config ; q git checkout FETCH_HEAD
       git config -f "project.config" \
           --add capability.viewTaskPaths "group Administrators"
 #     After migrating to version 3.5, it is no longer feasible to assign read permissions to
@@ -56,6 +79,8 @@
        --non-secret-group-without-space)  shift ; NON_SECRET_GROUP_NAME_WITHOUT_SPACE="$1" ;;
        --non-secret-group-with-space)     shift ; NON_SECRET_GROUP_NAME_WITH_SPACE="$1" ;;
        --secret-group)                    shift ; SECRET_GROUP_NAME="$1" ;;
+       --root-config-project)             shift ; ROOT_CONFIG_PRJ=$1 ;;
+       --root-config-branch)              shift ; ROOT_CONFIG_BRANCH=$1 ;;
        *)                                 die "invalid argument '$1'" ;;
    esac
    shift
@@ -66,8 +91,11 @@
 [ -z "$NON_SECRET_GROUP_NAME_WITHOUT_SPACE" ] && die "non-secret-group-without-space not set"
 [ -z "$NON_SECRET_GROUP_NAME_WITH_SPACE" ] && die "non-secret-group-with-space not set"
 [ -z "$SECRET_GROUP_NAME" ] && die "secret-group not set"
+[ -z "$ROOT_CONFIG_PRJ" ] && ROOT_CONFIG_PRJ=All-Projects
+[ -z "$ROOT_CONFIG_BRANCH" ] && ROOT_CONFIG_BRANCH=refs/meta/config
 
 "$USER_RUN_TESTS_DIR"/create-test-project-and-changes.sh
 "$USER_RUN_TESTS_DIR"/update-all-users-project.sh
 create_test_users_and_group
-setup_all_projects_repo
+setup_root_cfg_repo
+setup_capabilities
diff --git a/test/docker/run_tests/start.sh b/test/docker/run_tests/start.sh
index fadb29a..1abbd7e 100755
--- a/test/docker/run_tests/start.sh
+++ b/test/docker/run_tests/start.sh
@@ -9,8 +9,23 @@
 USER_RUN_TESTS_DIR="$USER_HOME"/"$RUN_TESTS_DIR"
 mkdir "$USER_HOME"/task && cp -r /task/{src,test} "$USER_HOME"/task
 
-if [ "$1" = "retest" ] ; then
-    cd "$USER_RUN_TESTS_DIR"/../../ && ./check_task_statuses.sh "$GERRIT_HOST"
+RETEST=false
+while (( "$#" )) ; do
+   case "$1" in
+       --retest)                          RETEST="true" ;;
+       --root-config-project)             shift ; ROOT_CONFIG_PRJ=$1 ;;
+       --root-config-branch)              shift ; ROOT_CONFIG_BRANCH=$1 ;;
+       *)                                 die "invalid argument '$1'" ;;
+   esac
+   shift
+done
+[ -z "$ROOT_CONFIG_PRJ" ] && ROOT_CONFIG_PRJ=All-Projects
+[ -z "$ROOT_CONFIG_BRANCH" ] && ROOT_CONFIG_BRANCH=refs/meta/config
+
+if [ "$RETEST" = "true" ] ; then
+    cd "$USER_RUN_TESTS_DIR"/../../ && \
+        ./check_task_statuses.sh "$GERRIT_HOST" \
+            --root-config-project "$ROOT_CONFIG_PRJ" --root-config-branch "$ROOT_CONFIG_BRANCH"
     exit $?
 fi
 
@@ -37,6 +52,7 @@
 GROUP_NAME_WITH_SPACE="test group"
 SECRET_GROUP="private_group"
 "$USER_RUN_TESTS_DIR"/create-one-time-test-data.sh --non-secret-user "$NON_SECRET_USER" \
+    --root-config-project "$ROOT_CONFIG_PRJ" --root-config-branch "$ROOT_CONFIG_BRANCH" \
     --untrusted-user "$UNTRUSTED_USER" --non-secret-group-without-space "$GROUP_NAME_WITHOUT_SPACE" \
     --non-secret-group-with-space "$GROUP_NAME_WITH_SPACE" --secret-group "$SECRET_GROUP"
 
@@ -46,10 +62,12 @@
 
 "$USER_RUN_TESTS_DIR"/../../check_task_statuses.sh \
     --server "$GERRIT_HOST" --non-secret-user "$NON_SECRET_USER" \
+    --root-config-project "$ROOT_CONFIG_PRJ" --root-config-branch "$ROOT_CONFIG_BRANCH" \
     --untrusted-user "$UNTRUSTED_USER" --non-secret-group-without-space "$GROUP_NAME_WITHOUT_SPACE" \
     --non-secret-group-with-space "$GROUP_NAME_WITH_SPACE" || RESULT=1
 
 "$USER_RUN_TESTS_DIR"/../../check_task_visibility.sh --server "$GERRIT_HOST" \
+    --root-config-project "$ROOT_CONFIG_PRJ" --root-config-branch "$ROOT_CONFIG_BRANCH" \
     --non-secret-user "$NON_SECRET_USER" --non-secret-group "$GROUP_NAME_WITHOUT_SPACE" \
     --secret-group "$SECRET_GROUP" || RESULT=1
 
diff --git a/test/lib/lib_helper.sh b/test/lib/lib_helper.sh
index 94157ad..249834c 100644
--- a/test/lib/lib_helper.sh
+++ b/test/lib/lib_helper.sh
@@ -221,6 +221,11 @@
     sed -e"s/testuser/$USER/"
 }
 
+replace_root_configs() { # < text_with_root_tokens > text_with_root_tokens_replaced
+ sed -e "s,{root-cfg-prj},$ROOT_CONFIG_PRJ," \
+     -e "s,{root-cfg-branch},$ROOT_CONFIG_BRANCH,"
+}
+
 get_user_ref() { # username > refs/users/<accountidshard>/<accountid>
     local user_account_id="$(curl --netrc --silent "http://$SERVER:$HTTP_PORT/a/accounts/$1" | \
     sed -e '1!b' -e "/^)]}'$/d" | jq ._account_id)"
@@ -236,7 +241,8 @@
 }
 
 replace_tokens() { # < text > text with replacing all tokens(changes, user)
-    replace_default_changes | replace_user_refs | replace_user | replace_groups
+    replace_default_changes | replace_user_refs | replace_user | replace_groups | \
+        replace_root_configs
 }
 
 strip_non_applicable() { ensure "$MYDIR"/strip_non_applicable.py ; } # < json > json