Files
roboco/tests/unit/agents/test_briefing_cluster_c4.py
T
5b27a443e9 chore(agnosticism): close the audit residue — B6/B8/B10 + three MAJORs (#587)
Thread the deployer's product name through the X reply + feature-spotlight
prompts (B6 leftover; release/video paths shipped in #570); make the
docs-site repo/URL config (ROBOCO_DOCS_SITE_*, defaults unchanged) instead
of a roboco-website hardcode (B8); de-assert our repo from the Main PM
prompt (B10); derive PR labels from the real target branch instead of
literal to-master/to-slave; drop the stale headcount from base.md; and
make the bash-guard's Makefile check require an actual quality/gate/lint/
test target before denying raw package-manager commands (no more
false-remediation loop on Go/Rust Makefiles).

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-19 17:52:17 +02:00

130 lines
5.9 KiB
Python

"""Cluster C4 — agent briefing/onboarding fixes (findings #3, #5, #9).
These assert on the COMPOSED system prompt (``compose_prompt``), which is
the text actually mounted into agent containers at ``/app/system-prompt.md``
via ``--system-prompt-file``. The three behaviors fixed here:
- #3: Main PM must read the upstream PO/HoM handoff BEFORE its own research,
so it does not duplicate the Board's analysis.
- #5: Main PM's Products-vs-Projects mental model — a Product fans out to
per-cell Projects which may be the SAME repo (monorepo subtrees) or
DIFFERENT repos (multi-repo); the Main PM coordinates across them and
must not assume one repo.
- #9: Developers know their exact workspace path convention, stay inside
their own cell workspace, and obtain secrets via the task description —
never ``env``/``printenv`` (bash-guard denies it).
"""
from __future__ import annotations
from roboco.agents.factories._base import compose_prompt
from roboco.models import AgentRole, Team
def _composed_prompt_for(role: AgentRole, team: Team | None = None) -> str:
return compose_prompt(role, team, agent_slug="test-agent")
# --------------------------------------------------------------------------
# #3 — Main PM consumes the upstream handoff before researching/planning
# --------------------------------------------------------------------------
def test_main_pm_prompt_requires_reading_upstream_handoff_first() -> None:
"""Main PM is told to read the PO/HoM handoff BEFORE its own research."""
prompt = _composed_prompt_for(AgentRole.MAIN_PM)
assert "Read the upstream handoff BEFORE you research or plan" in prompt
# Names both upstream sources explicitly.
assert "Product Owner" in prompt
assert "Head of Marketing" in prompt
def test_main_pm_prompt_forbids_re_researching_already_handed_work() -> None:
"""The duplicated-research failure mode is called out as an anti-pattern."""
prompt = _composed_prompt_for(AgentRole.MAIN_PM)
assert "Re-researching the codebase from scratch" in prompt
# The handoff lives in the task journal as decision/reflect entries.
assert "decision" in prompt and "reflect" in prompt
# --------------------------------------------------------------------------
# #5 — Products-vs-Projects mental model (mono- vs multi-repo)
# --------------------------------------------------------------------------
def test_main_pm_prompt_explains_product_fans_out_to_per_cell_projects() -> None:
"""Main PM prompt distinguishes Product (strategic) from per-cell Projects."""
prompt = _composed_prompt_for(AgentRole.MAIN_PM)
assert "Products vs Projects" in prompt
assert "fans out to one Project per cell" in prompt
def test_main_pm_prompt_covers_both_monorepo_and_multirepo() -> None:
"""Both fan-out shapes are described; neither is assumed by default."""
prompt = _composed_prompt_for(AgentRole.MAIN_PM)
assert "Monorepo" in prompt
assert "Multi-repo" in prompt
# Must not let the agent call a monorepo subtree "a separate repo".
assert "not" in prompt and "a separate repo" in prompt
def test_main_pm_prompt_frames_monorepo_as_per_deployment_fact() -> None:
"""The monorepo case is illustrated generically, not asserted as a fact
about a specific repo — the Main PM is told to confirm the shape from
each cell's ``project_slug``, never assume one deployment's config
(this repo's own `rennf93/roboco`) applies to every Product."""
prompt = _composed_prompt_for(AgentRole.MAIN_PM)
assert "per-deployment fact you confirm from each cell's" in prompt
assert "github.com/rennf93/roboco" not in prompt
# --------------------------------------------------------------------------
# #9 — Developer workspace path convention + secret handling
# --------------------------------------------------------------------------
def test_developer_prompt_states_exact_workspace_path_convention() -> None:
"""Developer prompt gives the exact /data/workspaces/<slug>/<team>/<slug> path."""
prompt = _composed_prompt_for(AgentRole.DEVELOPER, Team.BACKEND)
assert "/data/workspaces/<project-slug>/<team>/<agent-slug>/" in prompt
# Steered to stay inside its own cell workspace.
assert "Stay inside your own cell workspace" in prompt
def test_developer_prompt_forbids_probing_filesystem_for_workspace() -> None:
"""Developer is told not to probe / guess the workspace path."""
prompt = _composed_prompt_for(AgentRole.DEVELOPER, Team.BACKEND)
assert "Do NOT probe for it." in prompt
assert "ls /" in prompt and "find /" in prompt
def test_developer_prompt_gives_sanctioned_secret_path_not_env() -> None:
"""Secrets come via the task description; env/printenv is denied."""
prompt = _composed_prompt_for(AgentRole.DEVELOPER, Team.BACKEND)
# env/printenv called out as denied + bash-guard blocked.
assert "printenv" in prompt
assert "bash-guard" in prompt
# Sanctioned path: value provided in the task description.
assert "in the task description" in prompt
# Escalation route when the value is genuinely missing.
assert "i_am_blocked" in prompt
# --------------------------------------------------------------------------
# Main PM delegation description = brief (goal + constraints), not a solution
# --------------------------------------------------------------------------
def test_main_pm_prompt_delegation_description_is_a_brief() -> None:
"""The delegate description must be a goal+constraints brief, not a spec."""
prompt = _composed_prompt_for(AgentRole.MAIN_PM)
assert "The description is a **brief**, not a spec." in prompt
assert "Do NOT prescribe the cell's solution" in prompt
def test_main_pm_prompt_forbids_dictating_the_cells_design() -> None:
"""The design-task failure mode (PM dictating the UX layout) is called out."""
prompt = _composed_prompt_for(AgentRole.MAIN_PM)
assert "Design the UX and propose the layout" in prompt
assert "give them the problem, not your mockup" in prompt