fix(infra): mypy 2.3 compat; _ReleaseContext.prod_branch; deny bare uv sync; telegram compose flag

Renames the smoke-replay loop variable mypy 2.3's stricter narrowing
rejects (the uv.lock bump made this the promotion blocker), renames
_ReleaseContext.default_branch to prod_branch to match what it holds
since the env ladder, extends the Makefile-gated guard to bare uv sync
on both runtimes (shared-cache poisoning is the race the guard exists
for), and lists ROBOCO_TELEGRAM_ENABLED in both compose files
(byte-identical).
This commit is contained in:
Renn F
2026-07-15 08:25:34 +02:00
parent f1ff149b70
commit 236aab18f5
11 changed files with 44 additions and 20 deletions
@@ -16,6 +16,7 @@ from roboco.llm.providers.grok_cli_config import _RAW_PM_DENY, _deny_rules
def test_raw_pm_deny_rules_present() -> None:
rules = _deny_rules("developer")
assert "Bash(uv run*)" in rules
assert "Bash(uv sync*)" in rules
assert "Bash(uv pip install*)" in rules
assert "Bash(pip install*)" in rules
assert "Bash(conda install*)" in rules
+21 -7
View File
@@ -23,13 +23,14 @@ _DENIED = 2
_ALLOWED = 0
def _run(cmd: str) -> int:
def _run(cmd: str, cwd: Path | None = None) -> int:
payload = json.dumps({"tool_name": "Bash", "tool_input": {"command": cmd}})
result = subprocess.run(
[str(GUARD)],
input=payload,
capture_output=True,
text=True,
cwd=str(cwd) if cwd else None,
check=False,
)
return result.returncode
@@ -432,14 +433,22 @@ def test_blocks_app_mutation_even_in_grok_mode() -> None:
assert result.returncode == _DENIED
def test_allows_uv_sync_in_workspace() -> None:
"""Legit dependency sync in the agent's own workspace clone must pass."""
def test_denies_uv_sync_in_workspace_when_makefile_present() -> None:
"""W1: bare ``uv sync`` uses the shared ~/.cache/uv instead of the Makefile's
private UV_CACHE_DIR — the cache-poisoning race the guard exists to
prevent. Makefile-gated like ``uv run``: use ``uv sync --extra dev`` via
``make`` targets, not directly."""
assert (
_run("cd /data/workspaces/roboco/backend/be-dev-1 && uv sync --extra dev")
== _ALLOWED
== _DENIED
)
def test_allows_uv_sync_in_workspace_without_makefile(tmp_path: Path) -> None:
"""Makefile-less projects skip the deny — bare ``uv sync`` still passes."""
assert _run("uv sync --extra dev", cwd=tmp_path) == _ALLOWED
def test_denies_pip_install_when_makefile_present() -> None:
"""W1: raw ``pip install`` is Makefile-gated. A workspace clone carries
a ``Makefile`` (same repo), so a bare ``pip install`` is denied — agents
@@ -454,10 +463,15 @@ def test_allows_reading_files_under_app() -> None:
assert _run("ls -la /app/.venv/bin") == _ALLOWED
def test_allows_uv_sync_for_app_named_workspace_project() -> None:
def test_allows_uv_sync_for_app_named_workspace_project(tmp_path: Path) -> None:
"""A workspace path that merely contains 'app' (e.g. .../myapp/...) must not
trip the rule — the boundary requires /app to be its own path segment."""
assert _run("cd /data/workspaces/myapp/backend/be-dev-1 && uv sync") == _ALLOWED
trip the /app-boundary rule — the boundary requires /app to be its own path
segment. Run outside a Makefile-having cwd so the W1 raw-uv-sync gate
(a separate rule, covered above) doesn't also fire here."""
assert (
_run("cd /data/workspaces/myapp/backend/be-dev-1 && uv sync", cwd=tmp_path)
== _ALLOWED
)
# ---------------------------------------------------------------------------
+4 -4
View File
@@ -311,7 +311,7 @@ async def test_wait_for_ci_scoped_to_release_commit_not_branch_latest(
ctx = _ReleaseContext(
slug="roboco-api",
default_branch="master",
prod_branch="master",
root=tmp_path,
git_url="x",
git_prefix=[],
@@ -362,7 +362,7 @@ async def test_wait_for_ci_polls_through_rerun(
ctx = _ReleaseContext(
slug="roboco-api",
default_branch="master",
prod_branch="master",
root=tmp_path,
git_url="x",
git_prefix=[],
@@ -409,7 +409,7 @@ async def test_wait_for_ci_exhausts_window_on_persistent_failure(
ctx = _ReleaseContext(
slug="roboco-api",
default_branch="master",
prod_branch="master",
root=tmp_path,
git_url="x",
git_prefix=[],
@@ -538,7 +538,7 @@ async def test_release_push_argv_uses_extraheader_not_url_token(
ctx = _ReleaseContext(
slug="roboco-api",
default_branch="master",
prod_branch="master",
root=tmp_path,
git_url=git_url,
git_prefix=git_prefix,
@@ -16,7 +16,7 @@ from roboco.services.release_executor import _GitReleaseOps, _ReleaseContext
def _ctx() -> _ReleaseContext:
return _ReleaseContext(
slug="roboco",
default_branch="master",
prod_branch="master",
root=Path("/tmp/roboco-release-f012"),
git_url="https://github.com/o/roboco",
git_prefix=[],
@@ -32,7 +32,7 @@ class _FakeGitOps(_GitReleaseOps):
# Bypass the real __init__ (no session needed) — we only exercise
# commit_and_push, which calls self._git.
self._slug = ctx.slug
self._default_branch = ctx.default_branch
self._default_branch = ctx.prod_branch
self._root = ctx.root
self._git_url = ctx.git_url
self._git_prefix = ctx.git_prefix