refactor!: rename project to bot-bottle

Assisted-by: Codex
This commit is contained in:
2026-05-28 17:56:14 -04:00
parent 8875d8cc17
commit c08b09dc9f
200 changed files with 1271 additions and 1271 deletions
+12 -12
View File
@@ -7,7 +7,7 @@ an interactive claude session). Instead, this test stages the
minimum the orchestrator interacts with:
- A lightweight `alpine:latest sleep infinity` container named
`claude-bottle-<slug>` (matches the agent container name pattern)
`bot-bottle-<slug>` (matches the agent container name pattern)
on the per-bottle internal network.
- A marker file under `/home/node/.claude/` so we can assert the
transcript snapshot path actually transferred bytes.
@@ -31,15 +31,15 @@ import time
import unittest
from pathlib import Path
from claude_bottle import supervise
from claude_bottle.backend.docker import bottle_state, capability_apply
from claude_bottle.backend.docker.capability_apply import apply_capability_change
from claude_bottle.backend.docker.network import (
from bot_bottle import supervise
from bot_bottle.backend.docker import bottle_state, capability_apply
from bot_bottle.backend.docker.capability_apply import apply_capability_change
from bot_bottle.backend.docker.network import (
network_create_egress,
network_create_internal,
network_remove,
)
from claude_bottle.backend.docker.sidecar_bundle import (
from bot_bottle.backend.docker.sidecar_bundle import (
sidecar_bundle_container_name,
)
from tests._docker import skip_unless_docker
@@ -61,21 +61,21 @@ class TestCapabilityApply(unittest.TestCase):
def setUp(self):
self.slug = f"cb-test-cap-{os.getpid()}-{int(time.time())}"
self.agent_name = f"claude-bottle-{self.slug}"
self.agent_name = f"bot-bottle-{self.slug}"
self.sidecar_names: list[str] = []
self.internal_net = ""
self.egress_net = ""
# Fake home so tests don't touch ~/.claude-bottle/.
# Fake home so tests don't touch ~/.bot-bottle/.
self._tmp = tempfile.TemporaryDirectory(prefix="cap-apply-int.")
self._original_root = supervise.claude_bottle_root
self._original_root = supervise.bot_bottle_root
def fake_root() -> Path:
return Path(self._tmp.name) / ".claude-bottle"
return Path(self._tmp.name) / ".bot-bottle"
supervise.claude_bottle_root = fake_root # type: ignore[assignment]
supervise.bot_bottle_root = fake_root # type: ignore[assignment]
def tearDown(self):
supervise.claude_bottle_root = self._original_root # type: ignore[assignment]
supervise.bot_bottle_root = self._original_root # type: ignore[assignment]
for name in [self.agent_name, *self.sidecar_names]:
subprocess.run(
["docker", "rm", "-f", name],
+2 -2
View File
@@ -13,7 +13,7 @@ import os
import subprocess
import unittest
from claude_bottle.backend.docker.network import (
from bot_bottle.backend.docker.network import (
network_create_egress,
network_create_internal,
network_remove,
@@ -40,7 +40,7 @@ class TestOrphanCleanup(unittest.TestCase):
def test_remove_missing_is_noop(self):
# Returning True == idempotent success.
self.assertTrue(network_remove(f"claude-bottle-net-{self.slug}-does-not-exist"))
self.assertTrue(network_remove(f"bot-bottle-net-{self.slug}-does-not-exist"))
@unittest.skipIf(
os.environ.get("GITEA_ACTIONS") == "true",
@@ -20,7 +20,7 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.backend import BottleSpec, get_bottle_backend
from tests._docker import skip_unless_docker
from tests.fixtures import fixture_minimal
@@ -17,7 +17,7 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.backend import BottleSpec, get_bottle_backend
from tests._docker import skip_unless_docker
from tests.fixtures import fixture_minimal
+9 -9
View File
@@ -26,29 +26,29 @@ import time
import unittest
from pathlib import Path
from claude_bottle.backend.docker.bottle_state import pipelock_state_dir
from claude_bottle.backend.docker.network import (
from bot_bottle.backend.docker.bottle_state import pipelock_state_dir
from bot_bottle.backend.docker.network import (
network_create_egress,
network_create_internal,
network_remove,
)
from claude_bottle.backend.docker.pipelock import (
from bot_bottle.backend.docker.pipelock import (
PIPELOCK_CA_CERT_IN_CONTAINER,
PIPELOCK_CA_KEY_IN_CONTAINER,
pipelock_tls_init,
)
from claude_bottle.pipelock import PipelockProxy
from claude_bottle.backend.docker.pipelock_apply import (
from bot_bottle.pipelock import PipelockProxy
from bot_bottle.backend.docker.pipelock_apply import (
PipelockApplyError,
apply_allowlist_change,
fetch_current_allowlist,
fetch_current_yaml,
)
from claude_bottle.backend.docker.sidecar_bundle import (
from bot_bottle.backend.docker.sidecar_bundle import (
SIDECAR_BUNDLE_IMAGE,
sidecar_bundle_container_name,
)
from claude_bottle.yaml_subset import parse_yaml_subset
from bot_bottle.yaml_subset import parse_yaml_subset
from tests._docker import skip_unless_docker
from tests.fixtures import fixture_minimal
@@ -77,7 +77,7 @@ class TestPipelockApply(unittest.TestCase):
if n:
network_remove(n)
shutil.rmtree(self.work_dir, ignore_errors=True)
# Clean up the per-slug state dir under ~/.claude-bottle/state/
# Clean up the per-slug state dir under ~/.bot-bottle/state/
# (apply_allowlist_change writes there; _bring_up calls
# proxy.prepare with the same path so the bind-mount and the
# hot-reload write target stay coherent).
@@ -123,7 +123,7 @@ class TestPipelockApply(unittest.TestCase):
["docker", "create",
"--name", self.sidecar_name,
"--network", self.internal_net,
"-e", "CLAUDE_BOTTLE_SIDECAR_DAEMONS=pipelock",
"-e", "BOT_BOTTLE_SIDECAR_DAEMONS=pipelock",
"-v", f"{prep.yaml_path}:/etc/pipelock.yaml:ro",
"-v", f"{ca_cert_host}:{PIPELOCK_CA_CERT_IN_CONTAINER}:ro",
"-v", f"{ca_key_host}:{PIPELOCK_CA_KEY_IN_CONTAINER}:ro",
@@ -17,7 +17,7 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.backend import BottleSpec, get_bottle_backend
from tests._docker import skip_unless_docker
from tests.fixtures import fixture_minimal
@@ -27,8 +27,8 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from claude_bottle.manifest import Manifest
from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.manifest import Manifest
from tests._docker import skip_unless_docker
@@ -21,8 +21,8 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from claude_bottle.manifest import Manifest
from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.manifest import Manifest
from tests._docker import skip_unless_docker
@@ -22,8 +22,8 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from claude_bottle.manifest import Manifest
from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.manifest import Manifest
from tests._docker import skip_unless_docker
+7 -7
View File
@@ -12,7 +12,7 @@ asserts each one is blocked:
The suite is backend-agnostic — it goes through `get_bottle_backend()`
so a future smolmachines backend can be tested by setting
`CLAUDE_BOTTLE_BACKEND=smolmachines` without touching this file.
`BOT_BOTTLE_BACKEND=smolmachines` without touching this file.
PRD 0022 chunk 1 (this commit): fixture + setUpClass +
tearDownClass + preflight tool check. Attack tests land in
@@ -28,9 +28,9 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from claude_bottle.backend.docker.bottle_state import cleanup_state
from claude_bottle.manifest import Manifest
from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.backend.docker.bottle_state import cleanup_state
from bot_bottle.manifest import Manifest
from tests._docker import skip_unless_docker
@@ -78,16 +78,16 @@ class TestSandboxEscape(unittest.TestCase):
# already covers that. Smolmachines additionally needs smolvm on
# PATH and is macOS-only in v1 (libkrun/TSI). Skip cleanly when
# those are missing rather than die-ing inside backend.prepare.
backend_name = os.environ.get("CLAUDE_BOTTLE_BACKEND", "docker")
backend_name = os.environ.get("BOT_BOTTLE_BACKEND", "docker")
if backend_name == "smolmachines":
if sys.platform != "darwin":
raise unittest.SkipTest(
"CLAUDE_BOTTLE_BACKEND=smolmachines is macOS-only in "
"BOT_BOTTLE_BACKEND=smolmachines is macOS-only in "
"v1 (libkrun TSI)"
)
if shutil.which("smolvm") is None:
raise unittest.SkipTest(
"CLAUDE_BOTTLE_BACKEND=smolmachines requires `smolvm` "
"BOT_BOTTLE_BACKEND=smolmachines requires `smolvm` "
"on PATH: curl -sSL https://smolmachines.com/install.sh | sh"
)
@@ -1,6 +1,6 @@
"""Integration: end-to-end smoke for the PRD 0024 bundle shape.
Verifies that flipping `CLAUDE_BOTTLE_SIDECAR_BUNDLE=1` produces a
Verifies that flipping `BOT_BOTTLE_SIDECAR_BUNDLE=1` produces a
working bottle: `docker compose up` brings the agent + bundle pair
online, the four daemons inside the bundle bind their ports, and
the agent can reach pipelock + supervise via the bundle's network
@@ -21,8 +21,8 @@ import unittest
from pathlib import Path
from unittest.mock import patch
from claude_bottle.backend import BottleSpec, get_bottle_backend
from claude_bottle.manifest import Manifest
from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.manifest import Manifest
from tests._docker import skip_unless_docker
@@ -57,7 +57,7 @@ class TestSidecarBundleCompose(unittest.TestCase):
def test_bottle_up_with_bundle_flag_on(self):
stage_dir = Path(tempfile.mkdtemp(prefix="cb-bundle-smoke."))
try:
with patch.dict(os.environ, {"CLAUDE_BOTTLE_SIDECAR_BUNDLE": "1"}):
with patch.dict(os.environ, {"BOT_BOTTLE_SIDECAR_BUNDLE": "1"}):
backend = get_bottle_backend()
spec = BottleSpec(
manifest=_manifest(),
@@ -28,7 +28,7 @@ import unittest
from tests._docker import skip_unless_docker
_IMAGE = "claude-bottle-sidecars-test:chunk1"
_IMAGE = "bot-bottle-sidecars-test:chunk1"
_DOCKERFILE = "Dockerfile.sidecars"
@@ -108,7 +108,7 @@ class TestSidecarBundleImage(unittest.TestCase):
# ENTRYPOINT wiring works.
proc = subprocess.run(
["docker", "run", "--rm",
"-e", "CLAUDE_BOTTLE_SIDECAR_DAEMONS=nothing",
"-e", "BOT_BOTTLE_SIDECAR_DAEMONS=nothing",
_IMAGE],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
timeout=10.0,
@@ -18,7 +18,7 @@ import subprocess
import time
import unittest
from claude_bottle.backend.smolmachines.sidecar_bundle import (
from bot_bottle.backend.smolmachines.sidecar_bundle import (
BundleLaunchSpec,
bundle_container_name,
bundle_network_name,
@@ -47,13 +47,13 @@ class TestBundleBringup(unittest.TestCase):
remove_bundle_network(self.network)
def _bundle_image_built(self) -> bool:
"""The bundle image (`claude-bottle-sidecars:latest`) is
"""The bundle image (`bot-bottle-sidecars:latest`) is
built lazily by the docker backend's compose. If a
smolmachines-only operator hasn't run the docker backend
first, the image won't exist locally. Skip rather than
fail."""
r = subprocess.run(
["docker", "image", "inspect", "claude-bottle-sidecars:latest"],
["docker", "image", "inspect", "bot-bottle-sidecars:latest"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
check=False,
)
@@ -62,7 +62,7 @@ class TestBundleBringup(unittest.TestCase):
def test_create_network_then_start_bundle_pins_ip(self):
if not self._bundle_image_built():
self.skipTest(
"claude-bottle-sidecars:latest not built; run a docker "
"bot-bottle-sidecars:latest not built; run a docker "
"bottle first or `docker build -f Dockerfile.sidecars .`"
)
@@ -85,7 +85,7 @@ class TestBundleBringup(unittest.TestCase):
# Only run the pipelock daemon for this smoke — it's
# the lightest of the four and doesn't need bind
# mounts beyond what we'd skip without
# CLAUDE_BOTTLE_SIDECAR_DAEMONS. (The init
# BOT_BOTTLE_SIDECAR_DAEMONS. (The init
# supervisor will exit if pipelock fails to find its
# yaml — that's expected here; we just need the
# container to land on the network at the right IP.)
@@ -32,9 +32,9 @@ import tempfile
import unittest
from pathlib import Path
from claude_bottle.backend import BottleSpec, get_bottle_backend
from claude_bottle.backend.smolmachines.smolvm import is_available as _smolvm_available
from claude_bottle.manifest import Manifest
from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.backend.smolmachines.smolvm import is_available as _smolvm_available
from bot_bottle.manifest import Manifest
from tests._docker import skip_unless_docker
@@ -76,7 +76,7 @@ class TestSmolmachinesLaunch(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.stage = Path(tempfile.mkdtemp(prefix="cb-smol-launch."))
os.environ["CLAUDE_BOTTLE_BACKEND"] = "smolmachines"
os.environ["BOT_BOTTLE_BACKEND"] = "smolmachines"
backend = get_bottle_backend()
spec = BottleSpec(
manifest=_minimal_manifest(),
@@ -94,7 +94,7 @@ class TestSmolmachinesLaunch(unittest.TestCase):
cls._launch.__exit__(None, None, None)
finally:
shutil.rmtree(cls.stage, ignore_errors=True)
os.environ.pop("CLAUDE_BOTTLE_BACKEND", None)
os.environ.pop("BOT_BOTTLE_BACKEND", None)
def test_smoke_exec_echo(self):
# The plumbing-verifies-end-to-end smoke: a shell command
@@ -152,10 +152,10 @@ class TestSmolmachinesLaunch(unittest.TestCase):
def test_prompt_file_lands_in_guest(self):
# provision_prompt copies the host-side prompt.txt into the
# guest at /root/.claude-bottle-prompt.txt. The content
# guest at /root/.bot-bottle-prompt.txt. The content
# must match what the manifest declared so claude-code's
# --append-system-prompt-file reads the right text.
r = self.bottle.exec("cat /root/.claude-bottle-prompt.txt")
r = self.bottle.exec("cat /root/.bot-bottle-prompt.txt")
self.assertEqual(0, r.returncode, msg=r.stderr)
self.assertEqual(_AGENT_PROMPT, r.stdout.rstrip("\n"))
@@ -15,7 +15,7 @@ import platform
import subprocess
import unittest
from claude_bottle.backend.smolmachines.smolvm import is_available
from bot_bottle.backend.smolmachines.smolvm import is_available
@unittest.skipIf(