diff --git a/agents/.agents/skills/claude-subagent-playbook/references/codex-exec.md b/agents/.agents/skills/claude-subagent-playbook/references/codex-exec.md index 7472aa7..9c82392 100644 --- a/agents/.agents/skills/claude-subagent-playbook/references/codex-exec.md +++ b/agents/.agents/skills/claude-subagent-playbook/references/codex-exec.md @@ -15,6 +15,24 @@ WSL2 Ubuntu: exec/resume/`-o`/фоновый Bash — весь конвейер) ## Сквозные правила (любая ветка) +- **Предполёт — один раз на новой машине или в новом харнессе.** Две + проверки. Песочница: диагностика из + [codex-sandbox-apparmor.md](codex-sandbox-apparmor.md) (сигнатура поломки + и лечение — там же). Вывод доходит до файла: `codex --version > f; + wc -c f` — 18 байт значит, что рецепты ниже работают как написаны. Ноль + байт: среда теряет унаследованный файловый дескриптор (известный случай — + T3 Code, [lessons.md](lessons.md) 2026-08-06), и тогда каждый поток идёт + своим каналом в свой файл, а код возврата держит `pipefail`: + + ```bash + set -o pipefail + cat "$D/task-prompt.md" | codex exec --json … -o "$D/task-result.md" - \ + 2> >(cat > "$D/task-stderr.txt") | cat > "$D/task-stdout.jsonl" + ``` + + Без `pipefail` статус вернёт `cat`, и отказ Кодекса прочтётся успехом. + Подстановка процесса дописывает stderr асинхронно: читаешь этот файл тем + же вызовом — поставь `wait`. - **Промпт — файлом, шум — в файлы.** Промпт подавай через stdin (`-`): вход этого вызова занят промптом и должен оставаться открытым — правило `< /dev/null` для фоновых команд сюда не относится (закроешь — получишь @@ -103,9 +121,7 @@ WSL2 Ubuntu: exec/resume/`-o`/фоновый Bash — весь конвейер) паникует; полный фикс — AppArmor-профиль, и он под sudo: сам не применяй, дай пользователю готовые команды из [codex-sandbox-apparmor.md](codex-sandbox-apparmor.md) (`!`-префикс в чат), - после применения профиля напомни убрать `use_legacy_landlock`. Перед первым - прогоном конвейера на новой Linux-машине прогони оттуда же диагностику - превентивно. + после применения профиля напомни убрать `use_legacy_landlock`. - **Блок классификатора — сначала сузь песочницу, потом зови пользователя.** Первая проверка: нет ли в команде `-s danger-full-access` — классификатор его блокирует, и он не нужен (почему — в ветке исполнителя). Если блок не diff --git a/agents/.agents/skills/claude-subagent-playbook/references/lessons.md b/agents/.agents/skills/claude-subagent-playbook/references/lessons.md index 1395c3b..2d2c762 100644 --- a/agents/.agents/skills/claude-subagent-playbook/references/lessons.md +++ b/agents/.agents/skills/claude-subagent-playbook/references/lessons.md @@ -362,30 +362,16 @@ codex-cli 0.146.0 и была неверна. Развели две провер - **Не всякий харнесс — этот харнесс.** В Claude Code CLI 2.1.223 на той же машине и той же 0.146.1 всё штатно: файл на выходе — 18 байт, лог — семь строк, `- < prompt.md` читается. Ярлыку «я в агенте» не верь, спроси среду - сама, это пять секунд: - - ```bash - codex --version > f; wc -c f # 0 — среда теряет унаследованный файловый - # дескриптор, собирай вывод каналом - # 18 — рецепты codex-exec.md как написаны - ``` -- **Правило, годное в обеих средах**: и промпт, и вывод гнать каналами, - потоки при этом не сливать — - - ```bash - cat "$D/task-prompt.md" | codex exec --json … -o "$D/task-result.md" - \ - 2> >(cat > "$D/task-stderr.txt") | cat > "$D/task-stdout.jsonl" - ``` - - `2>&1 | cat` был бы короче и подложил бы ошибку разбора: строка stderr - в JSONL ломает `jq` и может встать первой вместо `thread.started`. В - разъехавшемся виде живут все три механизма рецептов — thread_id из первой - строки, сторож по росту лога, проверка отписки через `command_execution`. - Оговорка: подстановка процесса дописывает stderr асинхронно, перед чтением - этого файла в том же вызове нужен `wait`. Псевдотерминал из первой редакции - не нужен: `script` давал тот же JSONL ценой `\r` и непереносимости. Сами - рецепты `codex-exec.md` не правились — форма выше их не отменяет, а - дописывает сбор вывода. + сама — самотест стоит пять секунд и живёт предполётом в + [codex-exec.md](codex-exec.md). +- **Лечение живёт в рецептах, не здесь.** Форма сбора — каждый поток своим + каналом в свой файл — стоит там же, в предполёте, и включается по + результату самотеста; сами рецепты не правились. Три вещи, которые стоили + отдельной проверки. `2>&1 | cat` короче и подкладывает ошибку разбора: + строка stderr в JSONL ломает `jq` и может встать первой вместо + `thread.started`. Канал глотает код возврата (`false | cat` даёт ноль) — + отсюда `set -o pipefail` в форме. Псевдотерминал из первой редакции не + нужен: `script` давал тот же JSONL ценой `\r` и непереносимости. - **Механизм не вскрыт.** Известно поведение, не причина: файл на входе и выходе дочернего процесса пуст, канал цел, песочница ни при чём. Пишу это свойством среды и не выдаю за объяснение.