from types import SimpleNamespace
import pytest
from navi.config import Settings, settings
from navi.core.subagent_runner import _SCOPE_ANCHOR, build_subagent_system_prompt
def profile(system_prompt="You are the main agent.", subagent_system_prompt=""):
return SimpleNamespace(system_prompt=system_prompt, subagent_system_prompt=subagent_system_prompt)
class TestBuildSubagentSystemPrompt:
def test_uses_the_worker_prompt_when_the_profile_has_one(self):
text = build_subagent_system_prompt(
profile("main persona", "You are a worker.")
)
assert "You are a worker." in text
assert "main persona" not in text
assert _SCOPE_ANCHOR in text
def test_falls_back_to_the_main_prompt_without_a_worker_prompt(self):
"""The anchor used to be appended unconditionally, which made the
fallback dead code: a profile without a subagent prompt reached the
model as a bare scope rule with no role."""
text = build_subagent_system_prompt(profile("main persona"))
assert "main persona" in text
assert _SCOPE_ANCHOR in text
assert text.index("main persona") < text.index(_SCOPE_ANCHOR)
def test_inherit_adds_the_main_prompt_on_top_of_the_worker_prompt(self):
text = build_subagent_system_prompt(
profile("main persona", "You are a worker."),
inherit_system_prompt=True,
)
assert "main persona" in text
assert "You are a worker." in text
def test_custom_prompt_sits_before_the_anchor(self):
text = build_subagent_system_prompt(
profile("main persona"),
custom_system_prompt="You are a security auditor.",
)
assert "security auditor" in text
assert text.index("security auditor") < text.index(_SCOPE_ANCHOR)
def test_briefing_and_parent_context_are_appended_last(self):
text = build_subagent_system_prompt(
profile("main persona"),
briefing="Host: 10.0.0.5",
parent_session_id="s1",
)
assert "## Task context" in text
assert "Host: 10.0.0.5" in text
assert f"{settings.session_files_dir}/s1/" in text
assert text.index(_SCOPE_ANCHOR) < text.index("## Task context")
def test_every_part_is_separated(self):
text = build_subagent_system_prompt(
profile("main persona", "You are a worker."),
custom_system_prompt="Custom.",
briefing="Brief.",
)
assert text.count("\n\n---\n\n") == 3
@pytest.mark.parametrize(
("name", "expected"),
[
("subagent_thinking_stall_chars", 48_000),
("subagent_timeout_seconds", 600.0),
],
)
def test_settings_raise_the_old_guards(self, name, expected):
# 12k thinking chars / 300s cut healthy cloud-model runs; a regression
# here silently turns every long reasoning step into "stalled".
assert Settings.model_fields[name].default == expected