fix(tui): reject absolute paths in persona export/import and propagate typer.Exit
CI / lint (pull_request) Successful in 39s
CI / typecheck (pull_request) Successful in 1m3s
CI / security (pull_request) Successful in 1m15s
CI / quality (pull_request) Successful in 56s
CI / e2e_tests (pull_request) Successful in 5m10s
CI / build (pull_request) Successful in 48s
CI / integration_tests (pull_request) Successful in 6m59s
CI / helm (pull_request) Successful in 39s
CI / push-validation (pull_request) Successful in 28s
CI / unit_tests (pull_request) Failing after 8m55s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Successful in 13m25s
CI / status-check (pull_request) Failing after 5s

- registry.py: resolve_export_path/resolve_import_path now raise ValueError
  for absolute paths ("must be relative to current working directory"),
  fixing the two failing BDD scenarios in repl_input_modes.feature
- actor_run.py, actor.py: catch (click.exceptions.Exit, typer.Exit) so
  typer.Exit(code=2) from _resolve_config_files propagates with exit code 2
  instead of being swallowed by except Exception and re-raised as code 3
- Apply ruff format to actor_run_signature_resolve_steps.py,
  tui_persona_cycle_steps.py, state.py

ISSUES CLOSED: #10637
This commit is contained in:
2026-06-11 13:15:41 -04:00
parent 93707f08d2
commit f44d1bbd04
6 changed files with 30 additions and 21 deletions
@@ -28,6 +28,7 @@ with contextlib.suppress(ImportError, ModuleNotFoundError):
resolve_config_files,
)
# ---------------------------------------------------------------------------
@when("I call resolve_config_files with a config list")
def step_resolve_with_config_list(context: Any) -> None:
@@ -140,7 +141,9 @@ def step_resolve_with_no_config_data(context: Any) -> None:
context.resolve_exit_code = 0
except (SystemExit, click.exceptions.Exit) as exc:
context.resolve_exit_code = getattr(
exc, "exit_code", getattr(exc, "code", 1)
exc,
"exit_code",
getattr(exc, "code", 1),
)
context.resolve_stderr = " ".join(captured_stderr)
@@ -184,7 +187,9 @@ def step_resolve_unknown_actor_directly(context: Any) -> None:
context.resolve_exit_code = 0
except (SystemExit, click.exceptions.Exit) as exc:
context.resolve_exit_code = getattr(
exc, "exit_code", getattr(exc, "code", 1)
exc,
"exit_code",
getattr(exc, "code", 1),
)
context.resolve_stderr = " ".join(captured_stderr)
@@ -235,7 +240,9 @@ def step_resolve_with_empty_config_blob(context: Any) -> None:
context.resolve_exit_code = 0
except (SystemExit, click.exceptions.Exit) as exc:
context.resolve_exit_code = getattr(
exc, "exit_code", getattr(exc, "code", 1)
exc,
"exit_code",
getattr(exc, "code", 1),
)
context.resolve_stderr = " ".join(captured_stderr)
@@ -277,7 +284,9 @@ def step_resolve_with_empty_name(context: Any) -> None:
context.empty_name_exit_code = 0
except (SystemExit, typer.Exit) as exc:
context.empty_name_exit_code = getattr(
exc, "exit_code", getattr(exc, "code", 1)
exc,
"exit_code",
getattr(exc, "code", 1),
)
context.resolve_stderr = " ".join(captured_stderr)
@@ -331,7 +340,9 @@ def step_resolve_with_unserializable_config_blob(context: Any) -> None:
context.resolve_exit_code = 0
except (SystemExit, click.exceptions.Exit) as exc:
context.resolve_exit_code = getattr(
exc, "exit_code", getattr(exc, "code", 1)
exc,
"exit_code",
getattr(exc, "code", 1),
)
context.resolve_stderr = " ".join(captured_stderr)
+1 -3
View File
@@ -21,9 +21,7 @@ def _registry_for_temp_dir(path: Path) -> PersonaRegistry:
return PersonaRegistry(config_dir=path)
@given(
'I save TUI persona "{name}" with actor "{actor}" and cycle order {cycle:d}'
)
@given('I save TUI persona "{name}" with actor "{actor}" and cycle order {cycle:d}')
def step_save_persona_cycle(
context: Context, name: str, actor: str, cycle: int
) -> None:
+1 -1
View File
@@ -185,7 +185,7 @@ def run(
except UnsafeConfigurationError as exc:
typer.echo(f"Error: {exc}", err=True)
raise typer.Exit(code=1) from exc
except click.exceptions.Exit:
except (click.exceptions.Exit, typer.Exit):
raise
except CleverAgentsError as exc:
typer.echo(f"Error: {exc}", err=True)
+1 -1
View File
@@ -159,7 +159,7 @@ def run(
except UnsafeConfigurationError as exc:
typer.echo(f"Error: {exc}", err=True)
raise typer.Exit(code=1) from exc
except click.exceptions.Exit:
except (click.exceptions.Exit, typer.Exit):
raise
except CleverAgentsError as exc:
typer.echo(f"Error: {exc}", err=True)
+10 -10
View File
@@ -79,24 +79,24 @@ class PersonaRegistry:
return result
def resolve_export_path(self, output_path: Path) -> Path:
"""Resolve export path, accepting both absolute and relative paths."""
resolved = output_path.resolve()
# Allow absolute paths directly
"""Resolve export path; only relative paths within cwd are accepted."""
if output_path.is_absolute():
return resolved
# For relative paths, ensure they stay within working directory
raise ValueError(
"Export path must be relative to current working directory"
)
resolved = output_path.resolve()
base = Path.cwd().resolve()
if not resolved.is_relative_to(base):
raise ValueError("Export path must stay within working directory")
return resolved
def resolve_import_path(self, input_path: Path) -> Path:
"""Resolve import path, accepting both absolute and relative paths."""
resolved = input_path.resolve()
# Allow absolute paths directly
"""Resolve import path; only relative paths within cwd are accepted."""
if input_path.is_absolute():
return resolved
# For relative paths, ensure they stay within working directory
raise ValueError(
"Import path must be relative to current working directory"
)
resolved = input_path.resolve()
base = Path.cwd().resolve()
if not resolved.is_relative_to(base):
raise ValueError("Import path must stay within working directory")
+1 -1
View File
@@ -72,7 +72,7 @@ class PersonaState:
personas = self.registry.list_personas()
cyclic = sorted(
[p for p in personas if p.cycle_order > 0],
key=lambda p: p.cycle_order
key=lambda p: p.cycle_order,
)
if not cyclic: