fix(safety): gate browser safety guidance on the active mode, and close test gaps

The `agent_browser` skill is always loaded, so its safety paragraph shipped to
`off`-mode agents. Its prohibitions do not hold there — Strix only assigns a
browser session in a safety mode, while multi-session browsing is a normal
documented workflow — so the paragraph misdescribed the tools those agents
have. Move it into the already mode-gated block in the system prompt, and pin
the gating in both directions.

Test changes:

- `test_observe_mode_blocks_browser_click` asserted nothing about observe mode.
  The same call blocks in guarded for a different reason (no prior snapshot),
  so the observe rule was never reached. Give it a snapshot and assert the
  block's source and category, plus the passive-read inverse.
- Neither workspace-epoch bump was pinned; removing either left the suite
  green. Both are now covered, along with the read-only case that must not
  bump, and an end-to-end pairing where a patch during review invalidates a
  script decision.
- Cover `invoke_mutating_tool`'s observe-block and off-mode paths, the
  reviewer's low-confidence, block, missing-model and failed-inspection rules,
  the inline `bash -c` source path, and the two dependency-budget guards.
- Assert browser sessions are disjoint across agents rather than freezing one
  agent's command string.
- Fold the compound-separator and safety-config tests into the parametrized
  cases that already covered them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
oyasumi
2026-08-08 00:47:21 +00:00
co-authored by Claude Opus 5
parent 6e5bb2e76e
commit d45b99e551
7 changed files with 558 additions and 69 deletions
+10 -20
View File
@@ -166,27 +166,13 @@ def test_aliases_for_no_alias() -> None:
def test_apply_override_and_load_settings_round_trip(tmp_path: Path) -> None:
path = tmp_path / "cli-config.json"
path.write_text(
json.dumps({"env": {"STRIX_LLM": "round-trip-model", "PERPLEXITY_API_KEY": "pk"}}),
encoding="utf-8",
)
loader.apply_config_override(path)
settings = loader.load_settings()
assert settings.llm.model == "round-trip-model"
assert settings.integrations.perplexity_api_key == "pk"
# Second call is memoized -> same object.
assert loader.load_settings() is settings
def test_safety_settings_load_from_config(tmp_path: Path) -> None:
path = tmp_path / "cli-config.json"
path.write_text(
json.dumps(
{
"env": {
"STRIX_LLM": "round-trip-model",
"PERPLEXITY_API_KEY": "pk",
"STRIX_SAFETY_MODE": "guarded",
"STRIX_SAFETY_MODEL": "openai/safety-model",
"STRIX_SAFETY_TIMEOUT": "12",
@@ -197,11 +183,15 @@ def test_safety_settings_load_from_config(tmp_path: Path) -> None:
)
loader.apply_config_override(path)
settings = loader.load_settings().safety
settings = loader.load_settings()
assert settings.mode == "guarded"
assert settings.model == "openai/safety-model"
assert settings.timeout == 12
assert settings.llm.model == "round-trip-model"
assert settings.integrations.perplexity_api_key == "pk"
assert settings.safety.mode == "guarded"
assert settings.safety.model == "openai/safety-model"
assert settings.safety.timeout == 12
# Second call is memoized -> same object.
assert loader.load_settings() is settings
def test_apply_config_override_invalidates_cache(tmp_path: Path) -> None: