Commit Graph

5 Commits

Author SHA1 Message Date
Sylpheed RE agent
ddd220fa94 agent: stop the launcher claiming it cannot push when it can
The `loose` footer announced "cannot push -- no git credentials are mounted"
unconditionally. It was written before the credential mount existed and then
went stale, so with credentials correctly mounted it told the operator their
work would be lost -- the exact failure the mount was added to prevent.

It now reports what is actually true, and keeps the real warning for the case
where the file genuinely is missing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:22:02 +02:00
Sylpheed RE agent
797765a0b2 agent: move the loop prompt's memory into the corpus, and let it push
The autonomous agent's loop prompt had grown to ~9 000 words of accumulated
findings, refutations and traps. That is a symptom, not a style: it was doing
the job the corpus should do, in the one place that dies with the container.
Three concrete failures followed from it, and each gets a structural fix rather
than a louder instruction.

1. IT REPEATED WORK IT HAD ALREADY DONE.
   The "do not revive" list and the method traps existed ONLY in the prompt, so
   nothing pointed a fresh iteration at them. Extracted verbatim into two
   tracked files:

     docs/re/REFUTED.md  105 claims tested and dead, grouped by subject so a
                         grep for your noun finds the neighbourhood
     docs/re/METHOD.md   the traps already paid for -- controls, inference,
                         searching, reading data, runtime

   Both are linked from INDEX.md, and the loop prompt now opens by requiring
   them to be read. This is the fix for "re-derived something already known":
   the knowledge is now where the next iteration looks, not in a context window.

2. IT FORGOT TO ARM THE NEXT WAKEUP.
   The prompt tried to solve this by shouting at itself in the first line. The
   real fix is to stop asking: `loose` now defaults to a FIXED interval (45m),
   so the harness owns the cadence and a forgotten ScheduleWakeup cannot end the
   run. SYLPH_LOOP_INTERVAL= (empty) restores self-pacing.

3. IT COULD NOT PUBLISH, SO THE WORK ONLY EXISTED IN THE CONTAINER.
   New `push-work`, plus a read-only credentials mount
   (SYLPH_GIT_CREDENTIALS, default ~/.sylph-git-credentials). It pushes the
   CURRENT branch only, refuses anything that is not auto/*, and never
   force-pushes -- so the consolidated line stays a human's decision and a
   confused iteration cannot rewrite history. The loop prompt now requires a
   push on every iteration that commits, rather than at the end of some longer
   arc, which is exactly when a container dies.

The prompt itself drops from ~9 000 words to 85 lines and, more importantly,
stops accumulating: findings go in the corpus, and the prompt points at it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-28 15:46:59 +02:00
Sylpheed RE agent
7215cfd70a docker: two ways to talk to the loose agent
Remote Control, so a detached run is not a one-way trip:

  ./sylph-agent remote   -> https://claude.ai/code/session_...
  ./sylph-agent attach   -> the container's own terminal

`loose` now starts Claude Code with `--remote-control <name>`, registering the
session with your account so you can chat with it from claude.ai or a phone.
The name is passed EXPLICITLY because the flag's value is optional — a bare
`--remote-control` swallows the /loop prompt that follows as the session name.
Off with SYLPH_REMOTE=0, renamed with SYLPH_REMOTE_NAME.

The pty is forced to 200x50. A detached `docker run -t` gives 80x24, and Claude
Code hard-wraps to the terminal width — which truncated the Remote Control URL
to ".../session_01..." in the one place you actually need to read it, and made
`docker logs` nearly unusable besides.

`remote` waits up to six minutes and reports progress: registration lands a
minute or two after launch, so answering "not found" immediately would be
answering a different question than the one being asked.

Verified: a loose run registers and `./sylph-agent remote` prints the full URL.
`attach` is NOT verified end to end from here — `docker attach` refuses a piped
stdin ("cannot attach stdin to a TTY-enabled container"), which is a property of
this harness rather than of the container, so it needs a real terminal to try.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 20:06:05 +02:00
MechaCat02
cb6205ccfb 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>
2026-08-18 17:10:09 +02:00
MechaCat02
64f9d15610 docker: a container the autonomous RE agent can be turned loose in
Builds and runs both halves of the project -- Canary as the oracle, Reborn as
the port -- plus the dynamic-RE toolkit. Claude Code runs with
--dangerously-skip-permissions as an unprivileged `agent` user, because that
flag is refused under root.

Capped at half the machine, computed at launch: --cpus nproc/2, --memory half
of MemTotal with --memory-swap equal to it (no swap escape hatch -- a swapping
build thrashes the host, which is the failure the cap exists to prevent), and
build parallelism derived INSIDE the container from available memory rather
than core count, since a full-parallel build of this tree has OOM-killed the
host outright.

Three things the old box got wrong are fixed rather than reproduced: a real
toolchain (so rebuild_canary.sh's hand-relinking is obsolete), numpy and Pillow
(whose absence silently disabled every image oracle and looked like a logic
bug), and a display owned by PID 1 (so Xvfb no longer "dies on its own every
few minutes" -- it was being reaped because nothing owned it).

Verified end to end, not by inspection: the image builds, sylph-doctor is green,
`build-canary` links xenia_canary inside the container, and that binary then
runs -- guest memory and the JIT code cache appear in /dev/shm within 4 s,
1 205 log lines, gmem.py reads guest RAM, pad.py drives the file pad, and
screenshot captures the display.

Five environment defects found and fixed on the way, each of which fails in a
way that points somewhere else entirely:

  * /dev/shm is `noexec` under Docker. Xenia maps its JIT code cache out of an
    shm file, so it died with "Unable to allocate code cache generated code
    storage / Cannot initalize processor" -- which reads as an address-space
    clash, not a mount flag. Now `--tmpfs /dev/shm:rw,exec`.
  * An unknown xenia flag HANGS rather than errors: ParseLaunchArguments calls
    ShowSimpleMessageBox before logging is initialised, and that SDL dialog
    blocks on XIfEvent forever. `--audio` (which the RE notes recommend) is not
    a cvar in this tree; the symptom was a 10x10 window and an empty log.
  * Named volumes come up root-owned unless their mount point exists in the
    image, so the first cmake configure failed on pkgRedirects.
  * Ubuntu 24.04 ships its own uid-1000 account, colliding with the host user.
  * Ubuntu's spirv-opt has no --canonicalize-ids, so the shader step dies ~500
    objects in; the launcher mounts the host's LunarG SDK instead of baking one
    in, which also keeps shader output byte-identical to a host build.

Known limits, stated rather than papered over: on an NVIDIA host without the
NVIDIA Container Toolkit there is no hardware Vulkan (/dev/dri alone does
nothing for NVIDIA), and under lavapipe the emulator runs correctly but was not
observed to reach a rendered frame within a couple of minutes. gdb needs `sudo`
inside the container because the host's yama ptrace_scope outranks SYS_PTRACE.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 23:05:48 +02:00