mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[bug] agent image: stop baking VIRTUAL_ENV=/app/.venv (silences uv run warning)
The agent-base image baked VIRTUAL_ENV=/app/.venv globally (since 8e201901,
conventional venv-baking, no targeted rationale), so every bare uv run in a
workspace clone warned 'VIRTUAL_ENV=/app/.venv does not match project .venv
and will be ignored' on every gate run. uv used the workspace .venv correctly
(the warning was noise), but it flooded every agent transcript.
The load-bearing MCP/SDK pin is UV_PROJECT_ENVIRONMENT=/app/.venv + --no-sync
(added in #179, the actual startup-stall fix), NOT VIRTUAL_ENV — so removing
VIRTUAL_ENV does not regress #179. The gateway tools stay on PATH.
Verified by building the image (roboco-agent-base:venv-verify) and smoke-
testing in a real container: (1) VIRTUAL_ENV is empty, python resolves to
/app/.venv/bin/python via PATH; (2) bare uv run from /app imports a roboco
module with no warning; (3) the MCP-launch path — cwd with its OWN .venv —
UV_PROJECT_ENVIRONMENT=/app/.venv uv run --no-sync resolves to /app/.venv/
bin/python, overriding the cwd .venv (the exact MCP-server invariant), no
warning; (4) grep for 'does not match|will be ignored' across all output is
empty. bash-guard-tests.sh 64/0, ruff clean, test_workspace_uv_resolves_clone_venv 3/3.
Ships on next agent-image rebuild + redeploy.
This commit is contained in:
@@ -95,8 +95,11 @@ RUN echo '{}' > /home/agent/.claude.json
|
|||||||
# PYTHONUNBUFFERED: flush stdout/stderr immediately so the SDK driver's logs
|
# PYTHONUNBUFFERED: flush stdout/stderr immediately so the SDK driver's logs
|
||||||
# (e.g. the intake agent's turn-received / streamed lines) reach `docker logs` in
|
# (e.g. the intake agent's turn-received / streamed lines) reach `docker logs` in
|
||||||
# real time instead of block-buffering until the container is reaped.
|
# real time instead of block-buffering until the container is reaped.
|
||||||
|
# VIRTUAL_ENV is intentionally NOT baked: it made bare `uv run` in a workspace
|
||||||
|
# clone warn ("VIRTUAL_ENV=/app/.venv does not match project .venv") on every
|
||||||
|
# gate run. MCP/SDK pin to /app/.venv via UV_PROJECT_ENVIRONMENT (set in the
|
||||||
|
# orchestrator + sdk-startup-hook), not VIRTUAL_ENV. PATH keeps gateway tools.
|
||||||
ENV PATH="/app/.venv/bin:$PATH" \
|
ENV PATH="/app/.venv/bin:$PATH" \
|
||||||
VIRTUAL_ENV=/app/.venv \
|
|
||||||
PYTHONUNBUFFERED=1
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
# Claude Code uses mounted ~/.claude for auth.
|
# Claude Code uses mounted ~/.claude for auth.
|
||||||
|
|||||||
@@ -338,15 +338,14 @@ if echo "$low" | grep -qE '(^|[[:space:];&|])(uv[[:space:]]+(sync|lock|add|remov
|
|||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# `uv run` retargeting onto /app/.venv — same brick as the package-mutation
|
# `uv run --active` is denied as a footgun: the contract is bare `uv run`
|
||||||
# block above, via a verb that block doesn't list. In the agent container
|
# (workspace .venv, cwd-relative). VIRTUAL_ENV is no longer image-baked (it
|
||||||
# VIRTUAL_ENV=/app/.venv is baked globally, so `uv run --active` ALWAYS
|
# leaked into every workspace `uv run` as a warning), so --active has no active
|
||||||
# resolves onto /app/.venv and uv rebuilds it (be-dev-1 root cause,
|
# env and errors; an explicit /app target still bricks the gateway (next block).
|
||||||
# 2026-06-29). Also catch `uv run`/`uvx` with an explicit /app target.
|
|
||||||
# Bare `uv run` (workspace .venv, cwd-relative) is untouched.
|
# Bare `uv run` (workspace .venv, cwd-relative) is untouched.
|
||||||
if echo "$low" | grep -qE '(^|[[:space:];&|])uv[[:space:]]+run([[:space:]]|$)' && \
|
if echo "$low" | grep -qE '(^|[[:space:];&|])uv[[:space:]]+run([[:space:]]|$)' && \
|
||||||
echo "$low" | grep -qE '(^|[[:space:]=])--active([[:space:]]|$)'; then
|
echo "$low" | grep -qE '(^|[[:space:]=])--active([[:space:]]|$)'; then
|
||||||
echo "Denied: \`uv run --active\` retargets onto VIRTUAL_ENV=/app/.venv (the image-baked MCP-gateway venv) and rebuilds it, bricking your own gateway tools. Use bare \`uv run\` (it uses your workspace .venv under /data/workspaces, never /app). If /app's environment looks broken, report it via your blocked / escalation verb." >&2
|
echo "Denied: \`uv run --active\` is not the contract — use bare \`uv run\` (it uses your workspace .venv under /data/workspaces, never /app). If /app's environment looks broken, report it via your blocked / escalation verb." >&2
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
if echo "$low" | grep -qE '(^|[[:space:];&|])(uv[[:space:]]+run|uvx)([[:space:]]|$)' && \
|
if echo "$low" | grep -qE '(^|[[:space:];&|])(uv[[:space:]]+run|uvx)([[:space:]]|$)' && \
|
||||||
|
|||||||
@@ -100,8 +100,9 @@ run_case "deny rm -rf /etc" 2 "rm -rf /etc"
|
|||||||
run_case "deny uv sync --project /app" 2 "uv sync --project /app"
|
run_case "deny uv sync --project /app" 2 "uv sync --project /app"
|
||||||
run_case "deny uv pip install /app venv" 2 "uv pip install --python /app/.venv/bin/python foo"
|
run_case "deny uv pip install /app venv" 2 "uv pip install --python /app/.venv/bin/python foo"
|
||||||
run_case "deny cd /app && uv sync" 2 "cd /app && uv sync"
|
run_case "deny cd /app && uv sync" 2 "cd /app && uv sync"
|
||||||
# uv run --active: in the agent VIRTUAL_ENV=/app/.venv is baked globally, so
|
# uv run --active: not the contract (bare `uv run` uses the workspace .venv).
|
||||||
# --active ALWAYS retargets onto /app/.venv → uv rebuilds it → bricked gateway.
|
# VIRTUAL_ENV is no longer image-baked, so --active has no active env and
|
||||||
|
# errors; the guard still denies it with a clear remediation message.
|
||||||
run_case "deny uv run --active" 2 "uv run --active pytest"
|
run_case "deny uv run --active" 2 "uv run --active pytest"
|
||||||
run_case "deny uv run --active ruff" 2 "uv run --active ruff check ."
|
run_case "deny uv run --active ruff" 2 "uv run --active ruff check ."
|
||||||
run_case "deny env venv /app uv run active" 2 "VIRTUAL_ENV=/app/.venv uv run --active pytest"
|
run_case "deny env venv /app uv run active" 2 "VIRTUAL_ENV=/app/.venv uv run --active pytest"
|
||||||
|
|||||||
@@ -2920,9 +2920,8 @@ class AgentOrchestrator:
|
|||||||
# Every MCP server is launched as `uv run python -m
|
# Every MCP server is launched as `uv run python -m
|
||||||
# roboco.mcp.<server>` by Claude Code, with cwd = the agent's
|
# roboco.mcp.<server>` by Claude Code, with cwd = the agent's
|
||||||
# WORKSPACE (not /app). Without this, `uv run` resolves a
|
# WORKSPACE (not /app). Without this, `uv run` resolves a
|
||||||
# cwd-relative `.venv` (≠ the baked /app/.venv), ignores the
|
# cwd-relative `.venv` (≠ the pre-baked /app/.venv) and RE-SYNCS
|
||||||
# image's VIRTUAL_ENV with a warning, and RE-SYNCS the full
|
# the full dependency set (torch/lancedb/pyarrow/scipy, ~350MB) into a
|
||||||
# dependency set (torch/lancedb/pyarrow/scipy, ~350MB) into a
|
|
||||||
# fresh venv on every spawn — masked by a warm uv wheel cache,
|
# fresh venv on every spawn — masked by a warm uv wheel cache,
|
||||||
# but on a cold cache (first spawn after an image rebuild) the
|
# but on a cold cache (first spawn after an image rebuild) the
|
||||||
# download takes minutes and the MCP servers never come up
|
# download takes minutes and the MCP servers never come up
|
||||||
|
|||||||
Reference in New Issue
Block a user