'`; GET `/raw` → JSON с ключом `"tree"`; отменить задачу, порт освобождён.
+ - `test_serve_survives_usage_failure`: как выше, но `fetch_usage` бросает `FileNotFoundError`; GET `/limits` (опрос ≤ 3 с) содержит `"Не найден токен Claude Code"`; GET `/` → 200.
+ - `test_serve_busy_port_raises`: занять порт слушающим сокетом, `PORT` = он; `with self.assertRaises(OSError): asyncio.run(ca.serve())`.
+ - `test_register_toolbelt_kickstarts_when_port_dead`: фальшивый `iterm2` (`types.SimpleNamespace(tool=SimpleNamespace(async_register_web_view_tool=
))`), `port_alive → False`, `kickstart_service` — запись; `asyncio.run(ca.register_toolbelt(object()))` → kickstart вызван 1 раз; регистрация с `display_name="Claude"`, `identifier="com.andrey.claude-agents"`, `reveal_if_already_registered=False`, `url="http://127.0.0.1:8766/"` (при `PORT = 8766`).
+ - `test_register_toolbelt_skips_kickstart_when_alive`: `port_alive → True` → kickstart не вызван, регистрация вызвана.
+ - `test_kickstart_command`: `subprocess.run` подменён; `ca.kickstart_service()` → argv `["launchctl", "kickstart", f"gui/{os.getuid()}/com.andrey.claude-agents"]`; при `subprocess.run`, бросающем `OSError`, исключения наружу нет.
+ - `test_run_headless_calls_serve`: `serve` подменён async-функцией с флагом; `ca.run(["--headless"])` → флаг выставлен.
+ - `test_run_without_iterm_exits`: `iterm2 = None`; `with assertRaises(SystemExit): ca.run([])`.
+
+- [ ] **Step 2: Run tests to verify they fail**
+ Run: `python3 -m unittest tests.test_service -v`
+ Expected: FAIL / ERROR — `AttributeError: ... has no attribute 'serve'` и т.п.
+
+- [ ] **Step 3: Implement** перечисленное в Interfaces в `files/claude_agents.py`, рядом с бывшим `main`; добавить `import sys`.
+
+- [ ] **Step 4: Run tests to verify they pass**
+ Run: `python3 -m unittest discover -s tests -v`
+ Expected: все `OK` (включая Task 1).
+
+- [ ] **Step 5: Commit**
+ ```bash
+ git add tests/test_service.py files/claude_agents.py
+ git commit -m "feat: headless panel server and Toolbelt registrar entry points (#4)"
+ ```
+
+---
+
+### Task 3: Генератор LaunchAgent
+
+**Files:**
+- Create: `files/launchagent.py`
+- Create: `tests/test_launchagent.py`
+
+**Interfaces:**
+- Produces:
+ - `LABEL = "com.andrey.claude-agents"`
+ - `def plist(python: str, script: str, log: str) -> dict` → `{"Label": LABEL, "ProgramArguments": [python, script, "--headless"], "RunAtLoad": True, "KeepAlive": True, "StandardOutPath": log, "StandardErrorPath": log}`
+ - CLI: `python3 files/launchagent.py write