Replaces the cwd-hash identity with a random 5-char base36 suffix
per launch, so two simultaneous `start <agent>` invocations against
the same cwd no longer collide on container names. Each launch is
its own bottle.
State carries metadata: every prepare step writes
~/.claude-bottle/state/<identity>/metadata.json with the
(agent_name, cwd, copy_cwd, started_at) the bottle was launched
with. The new `cli.py resume <identity>` reads this metadata and
re-launches a bottle pinned to the same identity — picking up the
per-bottle Dockerfile (from a prior capability-block apply) and
the transcript snapshot under the same state dir.
- bottle_state.py: bottle_identity(agent_name) drops the cwd param
and gains a random suffix; BottleMetadata dataclass +
read/write/metadata_path helpers.
- BottleSpec gains an optional identity field — resume sets it to
pin the identity; start leaves it empty so prepare mints fresh.
- prepare.py: writes metadata at launch time; uses spec.identity if
provided (resume) else bottle_identity(agent_name) (fresh start).
- start.py: extracted _launch_bottle from cmd_start so resume can
share the launch core; prints `./cli.py resume <identity>` hint
at session end.
- cli/resume.py (new): reads metadata, reconstructs BottleSpec
with the recorded identity + cwd, delegates to _launch_bottle.
Errors clearly when no state exists for the given identity.
- cli/__init__.py: registers `resume` in COMMANDS + usage.
- dashboard.py: capability-block approval status line now appends
the `resume <identity>` hint so the operator can copy-paste the
rebuild command without leaving the TUI.
Closes the rebuild loop in PRD 0016: agent calls capability-block →
operator approves → bottle torn down with state preserved → status
line shows resume command → operator runs it → replacement bottle
boots with the new Dockerfile and prior transcript.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>