[codegen] Prohibit the "debug" configuration in experiment codegen (#37277)

This setting has no utility in general open source, but is still useful in other environments. This PR ensures that there are no `debug` configurations when the default codegen CI runs. This simplifies the release process as well.

Closes #37277

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37277 from drfloob:gen-exp-no-dbg 6517fec6e40971e4abb3e62999ed048ac5ffd0ea
PiperOrigin-RevId: 654928917
diff --git a/tools/codegen/core/experiments_compiler.py b/tools/codegen/core/experiments_compiler.py
index 2159324..3d3cc2e 100644
--- a/tools/codegen/core/experiments_compiler.py
+++ b/tools/codegen/core/experiments_compiler.py
@@ -261,16 +261,17 @@
             )
             self._error = True
             return False
-        is_dict = isinstance(rollout_attributes["default"], dict)
         for platform in allowed_platforms:
-            if is_dict:
+            if isinstance(rollout_attributes["default"], dict):
                 value = rollout_attributes["default"].get(platform, False)
+                if isinstance(value, dict):
+                    # debug is assumed for all rollouts with additional constraints
+                    self._default[platform] = "debug"
+                    self._additional_constraints[platform] = value
+                    continue
             else:
                 value = rollout_attributes["default"]
-            if isinstance(value, dict):
-                self._default[platform] = "debug"
-                self._additional_constraints[platform] = value
-            elif value not in allowed_defaults:
+            if value not in allowed_defaults:
                 print(
                     "ERROR: default for experiment %s on platform %s "
                     "is of incorrect format"
@@ -278,9 +279,8 @@
                 )
                 self._error = True
                 return False
-            else:
-                self._default[platform] = value
-                self._additional_constraints[platform] = {}
+            self._default[platform] = value
+            self._additional_constraints[platform] = {}
         return True
 
     @property
@@ -637,6 +637,14 @@
                 s.add(req)
         return s
 
+    def EnsureNoDebugExperiments(self):
+        for name, exp in self._experiment_definitions.items():
+            for platform, default in exp._default.items():
+                if default == "debug":
+                    raise ValueError(
+                        f"Debug experiments are prohibited. '{name}' is configured with {exp._default}"
+                    )
+
     def GenExperimentsBzl(self, mode, output_file):
         assert self._FinalizeExperiments()
         if self._bzl_list_for_defaults is None:
diff --git a/tools/codegen/core/gen_experiments.py b/tools/codegen/core/gen_experiments.py
index 2700004..0d4736e 100755
--- a/tools/codegen/core/gen_experiments.py
+++ b/tools/codegen/core/gen_experiments.py
@@ -87,6 +87,12 @@
         action="store_false",
         help="If specified, disables checking experiment expiry dates",
     )
+    flag_parser.add_argument(
+        "--no_dbg_experiments",
+        action="store_true",
+        help="Prohibit 'debug' configurations",
+        default=False,
+    )
     return flag_parser.parse_args(args)
 
 
@@ -162,6 +168,10 @@
             print("ERROR adding rollout spec")
             sys.exit(1)
 
+    if mode != "test" and args.no_dbg_experiments:
+        print("Ensuring no debug experiments are configured")
+        compiler.EnsureNoDebugExperiments()
+
     print(f"Mode = {mode} Generating experiments headers")
     compiler.GenerateExperimentsHdr(_EXPERIMENTS_HDR_FILE, mode)
 
diff --git a/tools/distrib/gen_experiments_and_format.sh b/tools/distrib/gen_experiments_and_format.sh
index f1fa544..d5dd5a7 100755
--- a/tools/distrib/gen_experiments_and_format.sh
+++ b/tools/distrib/gen_experiments_and_format.sh
@@ -15,7 +15,7 @@
 
 set -e
 cd $(dirname $0)/../..
-tools/codegen/core/gen_experiments.py --check
+tools/codegen/core/gen_experiments.py --check --no_dbg_experiments
 # clang format
 TEST='' \
     CHANGED_FILES="$(git status --porcelain | awk '{print $2}' | tr '\n' ' ')" \