The single point that computed `slug = slugify(agent_name)` in
prepare.py is now `slug = bottle_identity(agent_name, cwd)`. With
--cwd the identity has a sha256(resolved-cwd)[:12] suffix, so the
same agent against different projects gets distinct container
names, network names, queue dir, audit log paths, and per-bottle
state (Dockerfile + transcript). Without --cwd the identity is
just slugify(agent_name), unchanged from before — no-cwd bottles
look the same as today.
The downstream `slug` field on DockerBottlePlan keeps its name —
every module already threads it under "slug" and the value flowing
through is now the bottle's full identity. A comment in prepare.py
flags the change.
Fixes the bug surfaced in PR #22 review: running the same agent
against project-A's cwd then project-B's would silently share
project-A's per-bottle Dockerfile + transcript snapshot, container
name (forcing serialized runs), and queue/audit history.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 1 of PRD 0016. Lays the per-bottle state plumbing that
capability-block remediation will write into:
- claude_bottle/backend/docker/bottle_state.py: bottle_state_dir,
per_bottle_dockerfile (read), write_per_bottle_dockerfile,
per_bottle_image_tag (unique per slug), transcript_snapshot_dir.
Stores under ~/.claude-bottle/state/<slug>/.
- prepare.py: when a per-bottle Dockerfile exists, use
per_bottle_image_tag(slug) as the base image and pass the
per-bottle Dockerfile path through DockerBottlePlan.dockerfile_path.
--cwd still layers a derived image on top.
- launch.py: passes plan.dockerfile_path to build_image so the
per-bottle Dockerfile is what docker build reads.
- DockerBottlePlan gains dockerfile_path field; print() surfaces it
in the preflight summary so the operator can see at-a-glance that
this bottle is running on a rebuilt image.
Phase 2 will write to write_per_bottle_dockerfile (capability-block
approval); Phase 3 wires it into the dashboard.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>