Skip to content

Exec the harness through a pre-exec driver - #55

Open
CrypticSwarm wants to merge 3 commits into
harness-container-statefrom
harness-container-exec
Open

Exec the harness through a pre-exec driver#55
CrypticSwarm wants to merge 3 commits into
harness-container-statefrom
harness-container-exec

Conversation

@CrypticSwarm

Copy link
Copy Markdown
Owner

The exec line was the last place the entrypoint knew a harness by name: claude got a --settings/--setting-sources splice, two config-dir exports, and a PATH prefix for the git wrapper. That shaping now lives in claude's pre_exec hook, run by python3 -P -m swarmforge.harness.execute, which sets HOME, applies the hook, scrubs the launch-only variables (PYTHONPATH, PYTHONCOERCECLOCALE), resets the signal dispositions Python ignores, and execs the binary.

An invariant suite runs the driver per harness against a fake execve and pins the exact final argv and environment: claude gets precisely its settings flags, exports, and PATH prefix under the same guards the shell applied; the other three harnesses are byte-identical passthrough even with claude's artifacts staged; the launch variables appear nowhere. The entrypoint is down to timezone, user setup, the binary check, one init invocation, and one harness-agnostic exec — no harness names.

The harness binary is exec'd through a user-phase driver,
swarmforge.harness.execute: it sets HOME to the anvil home, drops the
variables its own launch adds (PYTHONPATH for the import root, and
PYTHONCOERCECLOCALE, pinned to 0 so interpreter startup cannot edit
LC_CTYPE into the environment the binary inherits), hands argv and
environment to the harness's pre_exec hook, and replaces itself with the
binary through execve. The default hook is the identity, so a harness
that declares nothing is exec'd byte-identically to a direct exec.

Claude's hook shapes both halves: the settings file rides the command
line ahead of the session's arguments with --setting-sources
user,project,local, the wrapper directory leads PATH exactly when the
root phase left an executable wrapper standing there, and the
environment names the merged config destination and the credential
store in the shared home.

The behavior suite runs the driver per harness with a recording execve
and compares argv and the whole environment: claude gets exactly its
deltas, every other harness passes through untouched, and the launch
variables reach no harness at all.
The entrypoint's exec drops privileges into
swarmforge.harness.execute rather than the binary itself, handing over
the harness name, the anvil home, and the session's arguments behind a
separator. The claude shell blocks -- the settings-flag splice, the
config-dir and credential-store exports, and the wrapper PATH shim --
are gone from the entrypoint; claude's pre_exec hook is what shapes
that exec now, and the entrypoint names no harness.

The launch sets exactly the variables the driver scrubs back out:
PYTHONPATH for the import root, and PYTHONCOERCECLOCALE=0 so
interpreter startup cannot edit LC_CTYPE into the environment the
binary inherits.

The text assertions that pinned the deleted shell move to the behavior
they guarded: the settings build and delivery are followed through the
module constant that ties them, the config home and credential store
are read out of the hook's environment, the wrapper switch-on is
observed as the PATH prefix itself, and a new agreement test parses
the exec line against execute.main's argv shape and the driver's
scrub list.
The interpreter sets SIGPIPE and SIGXFSZ to ignore at startup, and an
ignored disposition survives exec -- so the harness, and every process
it spawns, would see EPIPE write errors where a direct exec died
silently on a closed pipe. The driver defaults both back just before
execve, and the recording-execve tests restore them afterwards so the
suite does not keep running with the process's own dispositions
defaulted.

The passthrough invariant now runs with claude's settings file and
wrapper staged, so the whole-environment comparison proves the other
harnesses stay untouched by artifacts standing where claude's hook
would find them; a claude test exercises every delta at once, argv and
environment compared whole; and the selector the entrypoint guards is
pinned to the binary the driver execs -- one path only while every
harness's name is its binary.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant