Files
Sylpheed/docker/decoder/bin/claude-autonomous
MechaCat02 06676d3dc0
Some checks failed
CI / Native — ubuntu-latest (push) Failing after 7m48s
CI / WASM — Web (push) Failing after 7m16s
CI / Formatting (push) Failing after 1m15s
CI / Native — macos-latest (push) Has been cancelled
CI / Native — windows-latest (push) Has been cancelled
containers: each agent clones the monorepo into its own volume
The last structural fix for the collision class that has bitten three times. Both
containers now clone the repository into their OWN named volume instead of
bind-mounting a human's working tree, so an agent's local git config cannot
capture a human's commits, a credential helper cannot leak a container-only path
onto the host, and a `git add -A` cannot sweep another party's in-flight files.

Cloned once at startup and never auto-pulled: pulling under a running agent
moves files out from under whatever it is mid-edit, which is the same bug again.

Accepted knowingly: Claude Code keys per-project memory off the working
directory, so moving off the host path starts that memory empty. The corpus in
docs/ is the memory that matters and it travels with the clone.

Other changes:
* docker/agent -> docker/decoder; the launcher is sylph-decoder. Roles, not
  "the agent", now that there is more than one.
* /reborn is gone -- one repository now, so the port reads HANDOFF from its own
  checkout rather than through a live read-only mount of someone else's tree.
* Canary mounts separately at /canary; it stays a fork tracking upstream.
* A shared `sylpheed-exchange` volume at /exchange, with tools/ on PATH so
  `share` is available in both.
* The decoder's credential file gets the .host-copy treatment the port already
  had -- `credential.helper=store` rewrites by rename-over-target, which is
  EBUSY on a bind mount and reports a fatal that is not one.
* Budget split deliberately: decoder 5 cpu / 6 GB, port 3 / 4, leaving room for
  the planned Referee. "Half the host" was right when there was one agent.

Prompts move to docs/agents/ and are rewritten around the protocol: the oracle
is the running game, dynamic RE stays with the decoder, each iteration must
attempt to refute one claim of the other, and neither may verify its way out of
its own role.
2026-08-29 11:48:30 +02:00

73 lines
2.6 KiB
Plaintext
Executable File

#!/usr/bin/expect -f
# Start Claude Code for an unattended run, answering the first-run gates.
#
# Claude Code has three one-time interactive prompts, and every one of them is a
# silent, permanent hang for an agent with nobody at the keyboard — no error, no
# log line, just a container that looks healthy and does nothing:
#
# 1. the theme picker (first run, or whenever the installed version is
# newer than lastOnboardingVersion)
# 2. "do you trust this folder?" (per workspace)
# 3. the Bypass Permissions disclaimer (for --dangerously-skip-permissions)
#
# `seed-claude-config.py` pre-sets the config keys for 1 and 2. The disclaimer
# has no such key — it is meant to be accepted by a person once — so it is
# answered here instead. That is the honest reading of `sylph-agent loose`: the
# operator accepted it by choosing to run this, and the container is exactly the
# sandbox the warning asks for.
#
# ── Why the patterns are single words ──
# Claude Code draws its UI with ABSOLUTE COLUMN escapes between words, so the
# prompt arrives on the wire as
#
# 2.\x1b[8GYes,\x1b[13GI\x1b[15Gaccept
#
# A multi-word pattern like {Yes, I accept} therefore never matches, and the
# wrapper sits there looking like it is not running at all. Match one word.
set timeout 90
log_user 1
# Give the pty a wide, tall geometry. A detached `docker run -t` defaults to
# 80x24, and Claude Code hard-wraps to the terminal width — which truncates the
# Remote Control URL to "https://claude.ai/code/session_01…" in the one place
# you need to read it, and makes `docker logs` nearly unusable generally.
set stty_init "rows 50 cols 200"
set answered_theme 0
set answered_trust 0
set answered_bypass 0
spawn -noecho claude --dangerously-skip-permissions {*}$argv
expect {
-re {Choose} {
if {!$answered_theme} { set answered_theme 1; send "\r" }
exp_continue
}
-re {trust} {
if {!$answered_trust} {
set answered_trust 1
send_user "\n\[claude-autonomous] accepting the workspace trust prompt\n"
send "1\r"
}
exp_continue
}
-re {accept} {
if {!$answered_bypass} {
set answered_bypass 1
send_user "\n\[claude-autonomous] accepting the Bypass Permissions disclaimer\n"
send "2\r"
}
exp_continue
}
timeout {
# No new gate for a while: the session is up (or never had one). Stop
# matching so nothing later in the run can be answered by accident.
}
eof { exit }
}
# Hand the terminal over for the rest of the run.
interact