docker: turn the agent loose — detached /loop, with the first-run gates handled
`./sylph-agent loose [task]` starts Claude Code detached with
--dangerously-skip-permissions, running /loop on loop-task.md: work the RE
backlog one item at a time, commit to auto/* branches, never push, record
withdrawn results rather than deleting them. `logs`/`attach`/`stop` to watch and
end it. Runs -d WITHOUT --rm so the transcript survives the container exiting —
for an unattended run that is the only record of what happened.
Two things had to be fixed for an agent to survive being left alone.
MEMORY CONTINUITY. The project is now bind-mounted twice: at /work, and at its
own host path. Claude Code derives its per-project state key from the working
directory, so running at /work handed the agent an empty project instead of the
accumulated one. Verified: a loose run now reports MEMORY=yes and reads back the
same branch and backlog as the host.
FOUR INTERACTIVE GATES, each a silent permanent hang with nobody at the keyboard
-- no error, no log line, just a container that looks healthy and does nothing:
theme picker hasCompletedOnboarding + lastOnboardingVersion. Re-fires
whenever the container's Claude Code is a different version
to the host's, which is the normal case.
folder trust projects.<path>.hasTrustDialogAccepted
bypass disclaimer answered in a pty by bin/claude-autonomous. It has no config
key by design -- it wants a person to accept once, and the
person did so by launching this.
fullscreen upsell fullscreenUpsellSeenCount. This one fires MID-SESSION, after
the pty wrapper has already handed over, so it cannot be
answered the same way.
Config key names were read out of the shipped binary's own strings, not guessed.
The pty wrapper matches SINGLE WORDS. Claude Code draws its UI with
absolute-column escapes between words, so the prompt arrives as
`Yes,\x1b[13GI\x1b[15Gaccept` and a multi-word pattern never matches -- failing
in a way indistinguishable from the wrapper not running at all. It stops
matching once the session is live so nothing later is answered by accident.
~/.claude.json is now mounted read-only at a staging path and copied in, so the
container cannot rewrite the host config. Credentials stay shared read-write in
~/.claude, which is what token refresh and memory continuity need.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,49 @@ the emulator, reads its guest memory and photographs its screen.
|
||||
./sylph-agent doctor # prove it can do the four things it exists for
|
||||
./sylph-agent shell # poke around
|
||||
./sylph-agent agent # Claude Code, --dangerously-skip-permissions
|
||||
./sylph-agent loose # turn it loose: detached, /loop, self-paced
|
||||
./sylph-agent logs -f # watch it
|
||||
./sylph-agent stop # stop it
|
||||
```
|
||||
|
||||
## On the loose
|
||||
|
||||
`./sylph-agent loose` starts Claude Code **detached**, with
|
||||
`--dangerously-skip-permissions`, running `/loop` on the task in
|
||||
[`loop-task.md`](loop-task.md) — work the RE backlog one item at a time, commit
|
||||
to `auto/*` branches, never push, record withdrawn results rather than deleting
|
||||
them. Pass your own task as an argument, or set `SYLPH_LOOP_INTERVAL=30m` for a
|
||||
fixed cadence instead of letting it self-pace.
|
||||
|
||||
It runs `-d` **without** `--rm`, so the transcript survives the container
|
||||
exiting — for an unattended run that is the only record of what happened.
|
||||
`./sylph-agent attach` joins the live session (Ctrl-P Ctrl-Q to leave it
|
||||
running).
|
||||
|
||||
**It cannot push.** No git credentials are mounted, deliberately: a human
|
||||
reviews before anything leaves the box. Review with
|
||||
`git -C <project>/Syplheed-Reborn log --oneline main..auto/<topic>`.
|
||||
|
||||
### The four gates
|
||||
|
||||
Claude Code has four one-time prompts, and each one 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. All four are handled:
|
||||
|
||||
| gate | how |
|
||||
|---|---|
|
||||
| theme picker | `hasCompletedOnboarding` + `lastOnboardingVersion` in `~/.claude.json` |
|
||||
| "do you trust this folder?" | `projects.<path>.hasTrustDialogAccepted` |
|
||||
| Bypass Permissions disclaimer | answered in a pty by [`bin/claude-autonomous`](bin/claude-autonomous) — it has no config key, by design |
|
||||
| fullscreen-renderer upsell | `fullscreenUpsellSeenCount`, because it fires *mid-session*, after the pty wrapper has handed over |
|
||||
|
||||
Config keys were read out of the shipped binary's own strings rather than
|
||||
guessed. The pty wrapper matches **single words**: Claude Code draws its UI with
|
||||
absolute-column escapes between words, so `Yes, I accept` arrives as
|
||||
`Yes,\x1b[13GI\x1b[15Gaccept` and a multi-word pattern never matches — which
|
||||
looks exactly like the wrapper not running at all. It stops matching once the
|
||||
session is live, so nothing later can be answered by accident.
|
||||
|
||||
## The resource cap
|
||||
|
||||
The container gets **half the machine**, computed at launch so it stays half on
|
||||
@@ -137,6 +178,16 @@ needs to write). **That directory also holds your memory and project state**, so
|
||||
the container agent and you share it. Point `SYLPH_CLAUDE_HOME` at a separate
|
||||
directory to isolate it, or set `ANTHROPIC_API_KEY` instead.
|
||||
|
||||
`~/.claude.json` is different: mounted **read-only** at a staging path and
|
||||
copied in, so the container cannot rewrite your host config — and so a version
|
||||
skew between the container's Claude Code and yours cannot re-trigger onboarding.
|
||||
|
||||
The project is bind-mounted **twice**, at `/work` and at its own host path. The
|
||||
host path is what makes memory carry over: Claude Code derives its per-project
|
||||
state key from the working directory, so running at `/work` would hand the agent
|
||||
an empty project instead of the accumulated one. Verified — a loose run reports
|
||||
`MEMORY=yes` and reads back the same branch and backlog you see.
|
||||
|
||||
## Host prerequisites
|
||||
|
||||
* **A Vulkan SDK** (LunarG), for *building* only. Canary's shader step calls
|
||||
|
||||
Reference in New Issue
Block a user