Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- launch receipts (#499): `--goal SLUG` stamps intent at launch.
Exports `DEVA_GOAL` into the container (create-time env; attach
restamps when a fresh `--goal` is given) and appends one JSONL
receipt host-side to `$XDG_DATA_HOME/ccx/launches/YYYY-MM-DD.jsonl`
(`ts`, `goal`, `agent`, `cwd`, `container`, `source`). ccx joins
receipt->session by cwd+time so sessions stop being born orphans.
Receipt writes never block a launch; no `--goal` = zero change.

## [0.18.1] - 2026-07-28

### Fixed
Expand Down
7 changes: 7 additions & 0 deletions DEV-LOGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
- What: hoist generate_auth_tag() out of the container-name rewrite branch (it already produced `auth-default | auth-file-<stem> | api-key-<last4> | env` for naming and the `deva.auth_tag` label) and export it as `DEVA_AUTH_TAG` on every run. No naming/label change. Verified all three modes via scratch-HOME `--dry-run`.
- Result: in-container tooling gets a stable account handle. Statusline side (chip + per-account cache dirs keyed on the tag) lands in claude-code-statusline. /status stays wrong by design until a per-account `.claude.json` overlay exists — follow-up candidate for `--auth-with`.

# [2026-07-27] Dev Log: --goal launch receipts #499
- Why: every session is born an orphan — no record of which goal a launch serves, so attribution is post-hoc archaeology over JSONL. Stamp intent at launch instead; the ops-ledger loop (brief/debrief) needs this as its ground truth.
- What:
- `--goal SLUG` flag: slug validated `[a-z0-9][a-z0-9-]*` at parse; `DEVA_GOAL` exported at create; attach exec restamps only when a fresh `--goal` is passed (set-u-safe `${arr[@]+...}` expansion for the optional env pair).
- `write_launch_receipt`: one JSONL line to `$XDG_DATA_HOME/ccx/launches/YYYY-MM-DD.jsonl`, after the DRY_RUN gate, warning-only on failure. Receipts are evidence, so they live in the ccx data dir — the ops ledger cites session IDs, it does not store evidence.
- Result: verified live from a deva container against the host daemon: bad slug rejected, dry-run writes nothing, `-Q --goal smoke-goal claude -- --version` exits 0 and appends a correct receipt; `DEVA_GOAL=env-check` visible in assembled docker args. Not live-tested: the attach-exec restamp path (code-reviewed only). ccx-side join lands in thevibeworks/ccx.

# [2026-07-27] Dev Log: cloak release policy + closing the unverified VNC/daemon gap #456
- Why: two loose ends before shipping. (1) The 2026-07-21 entry left "x11vnc serving RFB and a live daemon inside the built image" unverified — that env couldn't apt-install. (2) release.yml made the GitHub Release `needs` the cloak build, so a ~200MB Chromium bake per arch (arm64 under QEMU) sat on the critical path of every release, including patch releases that touch nothing in that layer.
- What:
Expand Down
46 changes: 44 additions & 2 deletions deva.sh
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ DEBUG_MODE=false
DRY_RUN=false
CODEX_BROWSER_MCP=false
HOST_TMUX=false
GOAL=""

if [ -t 0 ] && [ -t 1 ]; then
DOCKER_TERMINAL_ARGS=(-it)
Expand Down Expand Up @@ -157,6 +158,10 @@ Deva flags:
-Q, --quick Bare mode: no host config mounts, no .deva loading, no autolink,
implies --rm. Like emacs -Q. Mutually exclusive with -c.
--host-net Use host networking for the agent container
--goal SLUG Stamp this launch with a goal slug ([a-z0-9-]):
exports DEVA_GOAL in-container and appends a
launch receipt to $XDG_DATA_HOME/ccx/launches/
for ccx session attribution
--host-tmux Opt in to container->host tmux: mounts ~/.ssh (ro)
and passes DEVA_HOST_USER so the in-container
deva-tmux CLI can reach the host tmux server.
Expand Down Expand Up @@ -3196,6 +3201,19 @@ parse_wrapper_args() {
i=$((i + 1))
continue
;;
--goal)
if [ $((i + 1)) -ge ${#incoming[@]} ]; then
echo "error: --goal requires a slug" >&2
exit 1
fi
Comment on lines +3204 to +3208
GOAL="${incoming[$((i + 1))]}"
if ! [[ "$GOAL" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
echo "error: --goal slug must match [a-z0-9][a-z0-9-]* (got '$GOAL')" >&2
exit 1
fi
i=$((i + 2))
continue
;;
--browser-mcp | --codex-browser-mcp | --with-browser)
CODEX_BROWSER_MCP=true
i=$((i + 1))
Expand Down Expand Up @@ -4064,6 +4082,7 @@ done
DOCKER_ARGS+=(-e "DEVA_CONTAINER_NAME=${CONTAINER_NAME}")
DOCKER_ARGS+=(-e "DEVA_WORKSPACE=$(pwd)")
DOCKER_ARGS+=(-e "DEVA_EPHEMERAL=${EPHEMERAL_MODE}")
[ -n "$GOAL" ] && DOCKER_ARGS+=(-e "DEVA_GOAL=${GOAL}")

# Back up .claude.json before mounting, without touching live credential files.
if [ "$QUICK_MODE" != true ] && [ "$DRY_RUN" != true ]; then
Expand Down Expand Up @@ -4206,6 +4225,24 @@ if [ "$DRY_RUN" = true ]; then
exit 0
fi

# Launch receipt: one JSONL line per --goal launch, host-side, into the
# ccx data dir. Receipts are evidence (ccx joins receipt->session by
# cwd+time); the ops ledger cites session IDs, it does not store these.
# Never blocks a launch: a failed write is a warning, not an error.
write_launch_receipt() {
[ -n "$GOAL" ] || return 0
local dir="${XDG_DATA_HOME:-$HOME/.local/share}/ccx/launches"
if ! mkdir -p "$dir" 2>/dev/null; then
echo "warning: cannot create $dir; skipping launch receipt" >&2
Comment on lines +4232 to +4236
return 0
fi
printf '{"ts":"%s","goal":"%s","agent":"%s","cwd":"%s","container":"%s","source":"deva"}\n' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$GOAL" "${ACTIVE_AGENT:-}" "$(pwd)" "$CONTAINER_NAME" \
Comment on lines +4239 to +4240

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Escape cwd before writing the JSONL receipt

When the workspace's absolute path contains a double quote, backslash, or newline—each valid in a Linux filename—$(pwd) is interpolated directly into the JSON string without escaping. This produces malformed or split JSONL records, so ccx cannot parse the receipt and the new session-attribution feature silently fails for that workspace; serialize these fields with a JSON encoder rather than printf interpolation.

Useful? React with 👍 / 👎.

>>"$dir/$(date -u +%Y-%m-%d).jsonl" 2>/dev/null ||
echo "warning: failed to write launch receipt" >&2
}
Comment on lines +4232 to +4243
write_launch_receipt

inject_workspace_context

if [ "$EPHEMERAL_MODE" = false ]; then
Expand Down Expand Up @@ -4274,17 +4311,22 @@ if [ "$EPHEMERAL_MODE" = false ]; then
_trace_env="DEVA_TRACE=0"
[ "${DEVA_TRACE_ACTIVE:-false}" = true ] && _trace_env="DEVA_TRACE=1"

# Same override rule for the goal: attach with a fresh --goal restamps
# the container; without it the create-time DEVA_GOAL (if any) stands.
_goal_env=()
[ -n "$GOAL" ] && _goal_env=(-e "DEVA_GOAL=${GOAL}")

# Trace UI reachability is fixed at container create (port publish);
# attaching to a container created without it cannot gain the mapping.
announce_trace_ui existing
announce_cloak_browser existing
announce_cloak_vnc existing

if [ "$AUTH_PROVISION_MODE" = true ]; then
docker exec -e "$_trace_env" "${DOCKER_TERMINAL_ARGS[@]}" "$CONTAINER_NAME" /usr/local/bin/docker-entrypoint.sh "${AGENT_COMMAND[@]}" || true
docker exec -e "$_trace_env" ${_goal_env[@]+"${_goal_env[@]}"} "${DOCKER_TERMINAL_ARGS[@]}" "$CONTAINER_NAME" /usr/local/bin/docker-entrypoint.sh "${AGENT_COMMAND[@]}" || true
finish_auth_provision
else
exec docker exec -e "$_trace_env" "${DOCKER_TERMINAL_ARGS[@]}" "$CONTAINER_NAME" /usr/local/bin/docker-entrypoint.sh "${AGENT_COMMAND[@]}"
exec docker exec -e "$_trace_env" ${_goal_env[@]+"${_goal_env[@]}"} "${DOCKER_TERMINAL_ARGS[@]}" "$CONTAINER_NAME" /usr/local/bin/docker-entrypoint.sh "${AGENT_COMMAND[@]}"
Comment on lines 4325 to +4329
fi
else
echo "Launching ${ACTIVE_AGENT} (ephemeral mode) via $(docker_image_ref)"
Expand Down
Loading