diff --git a/src/centpkg/cli.py b/src/centpkg/cli.py
index 0d5a58e..199cbc9 100755
--- a/src/centpkg/cli.py
+++ b/src/centpkg/cli.py
@@ -23,7 +23,11 @@ import centpkg.utils
 from pyrpkg.cli import cliClient
 from pyrpkg import rpkgError
 from six.moves.urllib_parse import urlparse
+import six.moves.configparser as ConfigParser
 
+import json
+import koji
+import os
 
 _DEFAULT_API_BASE_URL = 'https://gitlab.com'
 
@@ -126,6 +130,122 @@ class centpkgClient(cliClient):
             msg = "Remote with name '{0}' already exists."
         self.log.info(msg.format(remote_name))
 
+    # Overloaded build
+    def register_build(self):
+        # Do all the work from the super class
+        super(centpkgClient, self).register_build()
+        build_parser = self.subparsers.choices['build']
+        build_parser.formatter_class = argparse.RawDescriptionHelpFormatter
+        build_parser.description = textwrap.dedent('''
+            {0}
+
+            centpkg now sets the rhel metadata with --rhel-target.
+             * exception - This will build for the current in-development Y-stream release.
+               It is equivalent to passing latest when not in the Blocker and Exception Phase.
+             * zstream - If pre-GA of a y-stream release, this will build for 0day.
+               If post-GA of a Y-stream release, this will build for the Z-stream of that release.
+             * latest - This will always build for the next Y-stream release
+
+        '''.format('\n'.join(textwrap.wrap(build_parser.description))))
+
+        # Now add our additional option
+        build_parser.add_argument(
+            '--rhel-target',
+            choices=['exception', 'zstream', 'latest'],
+            default='latest',
+            help='Set the rhel-target metadata')
+
+    # Overloaded _build
+    def _build(self, sets=None):
+
+        # Only run if we have internal configuraions, or scratch
+        internal_config_file = "/etc/rpkg/centpkg_internal.conf"
+        if os.path.exists(internal_config_file):
+            # Get our internal only variables
+            cfg = ConfigParser.SafeConfigParser()
+            cfg.read(internal_config_file)
+            pp_api_url = config_get_safely(cfg, "centpkg.internal", 'pp_api_url')
+            gitbz_query_url = config_get_safely(cfg, "centpkg.internal", 'gitbz_query_url')
+            rhel_dist_git = config_get_safely(cfg, "centpkg.internal", 'rhel_dist_git')
+            
+            # Find out divergent branch and stabalization
+            stream_version = self.cmd.target.split('-')[0]
+            rhel_version = centpkg.utils.stream_mapping(stream_version)
+            active_y, in_stabilization = centpkg.utils.determine_active_y_version(rhel_version, pp_api_url)
+            divergent_branch = centpkg.utils.does_divergent_branch_exist(
+                                    self.cmd.repo_name, 
+                                    rhel_version, 
+                                    rhel_dist_git, 
+                                    pp_api_url, 
+                                    "rpms")
+
+            # Good to know
+            if divergent_branch :
+                print("divergent_branch: TRUE")
+            else:
+                print("divergent_branch: FALSE")
+            if in_stabilization :
+                print("in_stabilization: TRUE")
+            else:
+                print("in_stabilization: FALSE")
+            
+            # Update args.custom_user_metadata
+            if hasattr(self.args, 'custom_user_metadata') and self.args.custom_user_metadata:
+                try:
+                    temp_custom_user_metadata = json.loads(self.args.custom_user_metadata)
+                # Use ValueError instead of json.JSONDecodeError for Python 2 and 3 compatibility
+                except ValueError as e:
+                    self.parser.error("--custom-user-metadata is not valid JSON: %s" % e)
+                if not isinstance(temp_custom_user_metadata, dict):
+                    self.parser.error("--custom-user-metadata must be a JSON object")
+                if hasattr(self.args, 'rhel_target') and self.args.rhel_target:
+                    temp_custom_user_metadata["rhel-target"] = self.args.rhel_target
+                else:
+                    if divergent_branch and not in_stabilization :
+                        temp_custom_user_metadata["rhel-target"] = "latest"
+                    elif not divergent_branch and not in_stabilization :
+                        temp_custom_user_metadata["rhel-target"] = "zstream"
+                    else:
+                        print("We are currently in Stabalization mode")
+                        print("You must either set the rhel-target (--rhel-target)")
+                        print("or branch for the previous version.")
+                        print("Exiting")
+                        raise SystemExit
+                self.args.custom_user_metadata = json.dumps(temp_custom_user_metadata)
+            else:
+                if hasattr(self.args, 'rhel_target') and self.args.rhel_target:
+                    temp_custom_user_metadata = {"rhel-target": self.args.rhel_target}
+                    self.args.custom_user_metadata = json.dumps(temp_custom_user_metadata)
+                else:
+                    if divergent_branch and not in_stabilization :
+                        self.args.custom_user_metadata = '{"rhel-target": "latest"}'
+                    elif not divergent_branch and not in_stabilization :
+                        self.args.custom_user_metadata = '{"rhel-target": "zstream"}'
+                    else:
+                        print("We are currently in Stabalization mode")
+                        print("You must either set the rhel-target (--rhel-target)")
+                        print("or branch for the previous version.")
+                        print("Exiting")
+                        raise SystemExit
+                    
+
+            # Good to know, but take out for final cut
+            print('Metadata: %r', self.args.custom_user_metadata)
+
+            # Purposely fail during testing so we do not have so many builds
+            #self.args.custom_user_metadata = json.loads(self.args.custom_user_metadata)
+        
+        else:
+            if not self.args.scratch:
+                print("NO SCRATCH BUILD")
+                print("Only scratch builds are allowed without internal configurations")
+                print("Exiting")
+                raise SystemExit
+        
+        # Proceed with build
+        return super(centpkgClient, self)._build(sets)
+
+
     def register_request_gated_side_tag(self):
         """Register command line parser for subcommand request-gated-side-tag"""
         parser = self.subparsers.add_parser(
diff --git a/src/centpkg/utils.py b/src/centpkg/utils.py
index 1f9524d..b42b2ea 100644
--- a/src/centpkg/utils.py
+++ b/src/centpkg/utils.py
@@ -10,16 +10,22 @@
 # option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
 # the full text of the license.
 
-import re
-import json
-
 import git
+import json
+import logging
+import os
+import pytz
+import re
 import requests
+import sys
+from datetime import date, datetime
 from pyrpkg import rpkgError
 from requests.exceptions import ConnectionError
 from six.moves.configparser import NoOptionError, NoSectionError
 from six.moves.urllib.parse import quote_plus, urlparse
 
+import git as gitpython
+
 dist_git_config = None
 
 def do_fork(logger, base_url, token, repo_name, namespace, cli_name):
@@ -255,3 +261,174 @@ def get_repo_name(name, org='rpms'):
     repo_name = get_canonical_repo_name(dist_git_config, name)
 
     return '%s/%s' % (org, repo_name)
+
+def stream_mapping(csname):
+    """
+    Given a CentOS Stream name, map it to the corresponding RHEL name.
+
+    Parameters
+    ----------
+    csname: str
+        The CentOS Stream name.
+
+    Returns
+    -------
+    str
+        Correspoinding RHEL name.
+    """
+    if csname == "c8s" or csname == "cs8" :
+        return "rhel-8"
+    if csname == "c9s" or csname == "cs9" :
+        return "rhel-9"
+    if csname == "c10s" or csname == "cs10" :
+        return "rhel-10"
+    if csname == "c11s" or csname == "cs11" :
+        return "rhel-11"
+    return None
+
+def does_divergent_branch_exist(repo_name, rhel_version, rhel_dist_git, pp_api_url, namespace):
+    logger = logging.getLogger(__name__)
+
+    # Determine if the Y-1 branch exists for this repo
+
+    # Look up the Y-1 branch name
+    divergent_branch = determine_divergent_branch(
+        rhel_version,
+        pp_api_url,
+        namespace,
+    )
+    logger.debug("Divergent branch: {}".format(divergent_branch))
+    
+    g = gitpython.cmd.Git()
+    try:
+        g.ls_remote(
+            "--exit-code",
+            os.path.join(rhel_dist_git, namespace, repo_name),
+            divergent_branch,
+        )
+        branch_exists = True
+    except gitpython.GitCommandError as e:
+        t, v, tb = sys.exc_info()
+        # `git ls-remote --exit-code` returns "2" if it cannot find the ref
+        if e.status == 2:
+            branch_exists = False
+        else:
+            raise
+    return branch_exists
+
+def determine_divergent_branch(rhel_version, pp_api_url, namespace):
+    logger = logging.getLogger(__name__)
+
+    # Query the "package pages" API for the current active Y-stream release
+    # Phase 230 is "Planning / Development / Testing" (AKA DeveTestDoc)
+    request_params = {
+        "phase": 230,
+        "product__shortname": "rhel",
+        "relgroup__shortname": rhel_version,
+        "format": "json",
+    }
+
+    res = requests.get(
+        os.path.join(pp_api_url, "latest", "releases"),
+        params=request_params,
+        timeout=60,
+    )
+    res.raise_for_status()
+    payload = json.loads(res.text)
+    logger.debug(
+        "Response from PP API: {}".format(json.dumps(payload, indent=2))
+    )
+    if len(payload) < 1:
+        raise RuntimeError("Received zero potential release matches)")
+
+    active_y_version = -1
+    for entry in payload:
+        shortname = entry["shortname"]
+
+        # The shortname is in the form rhel-9-1.0
+        # Extract the active Y-stream version
+        m = re.search("(?<={}-)\d+(?=\.0)".format(rhel_version), shortname)
+        if not m:
+            raise RuntimeError(
+                "Could not determine active Y-stream version from shortname"
+            )
+        y_version = int(m.group(0))
+        if y_version > active_y_version:
+            active_y_version = y_version
+
+    # The divergent branch is Y-1
+    return "{}.{}.0".format(rhel_version, active_y_version - 1)
+
+def _datesplit(isodate):
+    date_string_tuple = isodate.split('-')
+    return [ int(x) for x in date_string_tuple ]
+
+
+def determine_active_y_version(rhel_version, pp_api_url):
+    """
+    Returns: A 2-tuple of the active Y-stream version(int) and whether we are
+    in the Exception Phase(bool)
+    """
+    logger = logging.getLogger(__name__)
+
+    # Query the "package pages" API for the current active Y-stream release
+    # Phase 230 is "Planning / Development / Testing" (AKA DeveTestDoc)
+    request_params = {
+        "phase": 230,
+        "product__shortname": "rhel",
+        "relgroup__shortname": rhel_version,
+        "format": "json",
+    }
+
+    res = requests.get(
+        os.path.join(pp_api_url, "latest", "releases"),
+        params=request_params,
+        timeout=60,
+    )
+    res.raise_for_status()
+    payload = json.loads(res.text)
+    logger.debug(
+        "Response from PP API: {}".format(json.dumps(payload, indent=2))
+    )
+    if len(payload) < 1:
+        raise RuntimeError("Received zero potential release matches)")
+
+    release_id = -1
+    active_y_version = -1
+    for entry in payload:
+        shortname = entry["shortname"]
+
+        # The shortname is in the form rhel-9-1.0
+        # Extract the active Y-stream version
+        m = re.search("(?<={}-)\d+(?=\.0)".format(rhel_version), shortname)
+        if not m:
+            raise RuntimeError(
+                "Could not determine active Y-stream version from shortname"
+            )
+        y_version = int(m.group(0))
+        if y_version > active_y_version:
+            active_y_version = y_version
+            release_id = entry["id"]
+
+    # Now look up whether we are in the Exception Phase for this Y-stream release
+    request_params = {
+        "name__regex": "Exception Phase",
+        "format": "json",
+    }
+    res = requests.get(os.path.join(pp_api_url, "latest", "releases", str(release_id), "schedule-tasks"), params=request_params)
+    res.raise_for_status()
+    payload = json.loads(res.text)
+
+    # This lookup *must* return exactly one value or the Product Pages are
+    # wrong and must be fixed.
+    assert len(payload) == 1
+
+    # Determine if this Y-stream release is in the exception phase
+    today = datetime.now(tz=pytz.utc).date()
+    exception_start_date = date(*_datesplit(payload[0]["date_start"]))
+    in_exception_phase = today >= exception_start_date
+
+    logger.debug("Active Y-stream: {}, Enforcing: {}".format(active_y_version, in_exception_phase))
+
+    return active_y_version, in_exception_phase
+