|
29 | 29 | # ── Discrete subcommand handlers (extracted from main() for clarity) ──────── |
30 | 30 |
|
31 | 31 |
|
| 32 | +def _resolve_persona(args, default: str = "adversarial") -> str | None: |
| 33 | + """Resolve the persona arg, honouring ``MAXIM_NO_DEFAULT_PERSONA``. |
| 34 | +
|
| 35 | + Returns ``None`` when ``MAXIM_NO_DEFAULT_PERSONA=1`` is set (i.e. the |
| 36 | + user passed ``--no-persona`` or set the env var directly). Callers |
| 37 | + that need a string for the orchestrator should coerce ``None`` to |
| 38 | + ``"neutral"`` (the empty-context built-in persona). The helper itself |
| 39 | + returns ``None`` so the V1 substrate-attribution report block can |
| 40 | + record ``persona_active: null`` without ambiguity — see |
| 41 | + ``docs/plans/confound_quarantine.md``. |
| 42 | + """ |
| 43 | + from maxim.runtime.confound_flags import default_persona_enabled |
| 44 | + |
| 45 | + if not default_persona_enabled(): |
| 46 | + return None |
| 47 | + return getattr(args, "sim_persona", default) or default |
| 48 | + |
| 49 | + |
32 | 50 | def _handle_list_models() -> int: |
33 | 51 | """Print all known LLM profiles grouped by backend, then return 0. |
34 | 52 |
|
@@ -621,6 +639,18 @@ def main(argv: Sequence[str] | None = None) -> int: |
621 | 639 |
|
622 | 640 | seed_all(args.seed) |
623 | 641 |
|
| 642 | + # ── Confound quarantine flags (V1 substrate-attribution) ──────────── |
| 643 | + # CLI flags --no-acting-coach and --no-persona are surface ergonomics |
| 644 | + # for the env vars consumed by maxim.runtime.confound_flags. Propagate |
| 645 | + # here (before any sim/agent dispatch) so worker construction and |
| 646 | + # persona resolution see the env var. Only set when the CLI flag is |
| 647 | + # truthy — never clear a pre-existing env var, so that env-only callers |
| 648 | + # (CI matrices, the harness wrapper script) keep working alongside CLI. |
| 649 | + if getattr(args, "no_acting_coach", False): |
| 650 | + os.environ["MAXIM_DISABLE_ACTING_COACH"] = "1" |
| 651 | + if getattr(args, "no_persona", False): |
| 652 | + os.environ["MAXIM_NO_DEFAULT_PERSONA"] = "1" |
| 653 | + |
624 | 654 | # ── Force-kill on double Ctrl+C ────────────────────────────────── |
625 | 655 | # First Ctrl+C signals the LLM cancellation primitive and raises |
626 | 656 | # KeyboardInterrupt in the main thread for graceful shutdown. If the |
@@ -828,7 +858,7 @@ def _force_exit_handler(signum, frame): |
828 | 858 | runs=getattr(args, "runs", 1) or 1, |
829 | 859 | output_dir=getattr(args, "benchmark_output", None), |
830 | 860 | baseline_path=getattr(args, "baseline", None), |
831 | | - persona=getattr(args, "sim_persona", "campaign") or "campaign", |
| 861 | + persona=_resolve_persona(args, default="campaign") or "neutral", |
832 | 862 | max_turns=50, |
833 | 863 | response_timeout=60.0, |
834 | 864 | debug=bool(getattr(args, "debug", "")), |
@@ -1145,7 +1175,11 @@ def _force_exit_handler(signum, frame): |
1145 | 1175 | # so the narrator drives multi-turn structured phases. |
1146 | 1176 | from maxim.simulation.orchestrator import start_simulation_mode |
1147 | 1177 |
|
1148 | | - persona = getattr(args, "sim_persona", "campaign") |
| 1178 | + # `_resolve_persona` returns None when --no-persona / |
| 1179 | + # MAXIM_NO_DEFAULT_PERSONA=1; coerce to "neutral" (empty |
| 1180 | + # context_prompt) so the orchestrator's get_persona() lookup |
| 1181 | + # succeeds. |
| 1182 | + persona = _resolve_persona(args, default="campaign") or "neutral" |
1149 | 1183 | debug = bool(_debug_raw) |
1150 | 1184 | resume_sim = getattr(args, "resume_sim", None) |
1151 | 1185 |
|
@@ -1183,7 +1217,10 @@ def _force_exit_handler(signum, frame): |
1183 | 1217 | from maxim.simulation.orchestrator import start_simulation_mode |
1184 | 1218 |
|
1185 | 1219 | goal = getattr(args, "sim_goal", None) or "test the agent's capabilities" |
1186 | | - persona = getattr(args, "sim_persona", "adversarial") |
| 1220 | + # `_resolve_persona` returns None when --no-persona / |
| 1221 | + # MAXIM_NO_DEFAULT_PERSONA=1; coerce to "neutral" so |
| 1222 | + # get_persona() succeeds without injecting adversarial framing. |
| 1223 | + persona = _resolve_persona(args, default="adversarial") or "neutral" |
1187 | 1224 | debug = bool(_debug_raw) |
1188 | 1225 | resume_sim = getattr(args, "resume_sim", None) |
1189 | 1226 |
|
@@ -1229,7 +1266,7 @@ def _force_exit_handler(signum, frame): |
1229 | 1266 | runs=getattr(args, "runs", 1) or 1, |
1230 | 1267 | output_dir=getattr(args, "benchmark_output", None), |
1231 | 1268 | baseline_path=getattr(args, "baseline", None), |
1232 | | - persona=getattr(args, "sim_persona", "campaign") or "campaign", |
| 1269 | + persona=_resolve_persona(args, default="campaign") or "neutral", |
1233 | 1270 | max_turns=50, |
1234 | 1271 | response_timeout=60.0, |
1235 | 1272 | debug=bool(_debug_raw), |
|
0 commit comments