Files
ai-agent/sidecar/test_claude_args.py
Gabriel Vidal 1de94265d4 fix(sidecar): one arg builder for spawn / resume / fork — the spawn missed --settings
The spawn path kept its own arg list and never got the --settings
payload, so a freshly spawned run had neither the stop guard nor the
inbox opt-in (its background shell died at once, /message was accepted
by the socket and dropped). _claude_args builds the shared part for all
three launch paths; a test pins that no literal arg list is left.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 23:32:04 +02:00

62 lines
2.4 KiB
Python

"""Every claude launch path builds on _claude_args: the --settings payload
(stop guard hook + inbox opt-in) and the headless flags can't be missed by
one of spawn / resume / fork — the spawn path once was."""
import json
import re
import sidecar
SID = "11111111-2222-3333-4444-555555555555"
def _settings(args):
assert args.count("--settings") == 1
return json.loads(args[args.index("--settings") + 1])
def test_spawn_and_resume_shapes_share_the_flags():
spawn = sidecar._claude_args("hi", "--session-id", SID)
resume = sidecar._claude_args("hi", "--resume", SID)
assert spawn[:5] == [sidecar.CLAUDE_BIN, "-p", "hi", "--session-id", SID]
assert resume[:5] == [sidecar.CLAUDE_BIN, "-p", "hi", "--resume", SID]
for a in (spawn, resume):
assert a[a.index("--output-format") + 1] == "json"
assert a[a.index("--permission-mode") + 1] == sidecar.PERMISSION_MODE
if sidecar.DISALLOWED_TOOLS:
assert " ".join(sidecar.DISALLOWED_TOOLS) in a
s = _settings(a)
assert s["crossSessionInbound"] == "accept"
def test_settings_carry_the_stop_guard_hook():
s = _settings(sidecar._claude_args("hi", "--session-id", SID))
hooks = s["hooks"]["Stop"][0]["hooks"]
assert len(hooks) == 1
cmd = hooks[0]["command"]
assert cmd.endswith("stop_guard.py") and "python" in cmd
assert hooks[0]["timeout"] == sidecar.STOP_GUARD_TIMEOUT_S
assert hooks[0]["type"] == "command"
def test_stop_guard_can_be_switched_off(monkeypatch):
monkeypatch.setattr(sidecar, "STOP_GUARD_ENABLED", False)
s = _settings(sidecar._claude_args("hi", "--session-id", SID))
assert "hooks" not in s and s["crossSessionInbound"] == "accept"
def test_every_launch_site_uses_the_builder():
src = open(sidecar.__file__, encoding="utf-8").read()
# the only literal `CLAUDE_BIN, "-p"` is the builder's own
assert len(re.findall(r'CLAUDE_BIN, "-p"', src)) == 1
assert src.count("_claude_args(prompt, ") == 3 # spawn, resume, fork
def test_run_env_exports_the_wait_ceiling(monkeypatch):
monkeypatch.setattr(sidecar, "BG_WAIT_CEILING_MS", "7200000")
env = sidecar._run_env(SID, None)
assert env["CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS"] == "7200000"
monkeypatch.setattr(sidecar, "BG_WAIT_CEILING_MS", "")
monkeypatch.delenv("CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS", raising=False)
assert "CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS" not in sidecar._run_env(SID, None)