mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
* feat(sandbox): on-demand request_sandbox verb replaces eager provisioning Sandboxes were provisioned at every agent spawn for opted-in projects, so every role paid the sidecar spin-up and a provisioning failure refused the spawn. Provisioning now happens when an agent asks: the request_sandbox do-verb (dev + QA) reaches the orchestrator through ContentActionsDeps, ensure_sandbox provisions idempotently with an in-memory per-agent cache (evicted at teardown and janitor sweep), and creds return in the envelope payload including ready-to-export ROBOCO_TEST_* values. Spawn now only injects a marker env naming the available services plus a briefing line; sandbox failures can no longer refuse a spawn. Teardown lifecycle unchanged. * feat(sandbox): harden request_sandbox + Phase 3 wiring proof and docs Hardening from adversarial review: ensure_sandbox now provisions the project's full opted-in set on first request (a later superset can never tear down a live sandbox mid-use), serializes per-agent behind an asyncio lock (a client timeout-retry no longer races its own in-flight provision), and verifies container liveness on every cache hit (a dead sandbox evicts and re-provisions instead of serving dead creds). MCP client budget 720->1080s for the full-set cold case. Phase 3: e2e smoke wiring test (manifest grants + guard-chain envelopes over the real API), sandbox-db/tools/map docs and CLAUDE.md rewritten for on-demand. * feat(sandbox): release sandboxes when the agent's work ends CEO directive: sidecars must not dangle once the agent is done. The six work-ending verbs (i_am_done, unclaim, i_am_idle, pass_review, fail_review, i_documented) now release the caller's sandbox best-effort on their success path via release_sandbox (lock + teardown + cache evict; a no-sandbox agent costs a dict lookup). Container removal and the janitor remain the backstop; a re-request provisions fresh. * test(sandbox): monkeypatch the release hook instead of method assignment mypy method-assign rejected the direct AsyncMock assignments; the prior static gate ran before this test file landed. * test(sandbox): guard envelope evidence for mypy in verb tests * chore(prompts): regenerate verb tables for request_sandbox * chore: resolve merge with master (breadcrumbs + statement budget) --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
41 lines
1.3 KiB
Python
41 lines
1.3 KiB
Python
"""`_write_agent_briefing`'s sandbox availability line.
|
|
|
|
Names the `request_sandbox` verb for an opted-in project (spec's "name it —
|
|
cheap and kills a discovery failure mode" default) and is silent otherwise.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from roboco.runtime.orchestrator import AgentOrchestrator
|
|
|
|
|
|
def _orch() -> AgentOrchestrator:
|
|
with patch.object(AgentOrchestrator, "__init__", return_value=None):
|
|
orch = AgentOrchestrator.__new__(AgentOrchestrator)
|
|
object.__setattr__(orch, "_TOOL_LOAD_CACHE", {})
|
|
return orch
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_briefing_names_request_sandbox_when_opted_in(tmp_path: object) -> None:
|
|
orch = _orch()
|
|
path = await orch._write_agent_briefing(
|
|
"dev-1", None, str(tmp_path), ["postgres", "redis"]
|
|
)
|
|
assert path is not None
|
|
content = path.read_text()
|
|
assert "request_sandbox()" in content
|
|
assert "postgres, redis" in content
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_briefing_omits_sandbox_line_when_not_opted_in(tmp_path: object) -> None:
|
|
orch = _orch()
|
|
path = await orch._write_agent_briefing("dev-1", None, str(tmp_path), [])
|
|
assert path is not None
|
|
content = path.read_text()
|
|
assert "request_sandbox()" not in content
|