- Зачем:
- ретро трёх циклов конвейера показало: находки ревью проходили через
контекст оркестратора до шести раз, review-промпт дублировался, ~7
пустых ходов на idle-пингах teammate-ревьюера.
- Что:
- SKILL.md: механика «индекс у оркестратора, нагрузка файлами» — вердикты
в файлы обменного temp-каталога, fix-промпт конкатенацией, один
канонический review-промпт на обоих ревьюеров, idle-пинги как шум;
- новый references/prompt-templates.md — скелеты четырёх промптов;
- codex-exec.md: сборка промптов cat'ом, боевая модель gpt-5.5 явно
(фронтир из конфига не наследовать);
- codex-sandbox-fix-handover: рабочий AppArmor-фикс (ловушка memfd,
профиль на бинарь codex), проверен на обоих режимах;
- lessons.md: датированные записи ретро, казуса Sol и memfd.
- Проверка:
- чтение SKILL.md и references/, следующий прогон конвейера.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 KiB
Codex через прямой codex exec: рецепты и ловушки
Проверено 2026-07-09 (codex-cli 0.142.5, Windows + Git Bash); линукс-эмпирика
прогонов 07-06/07-07 шла через companion-обёртку и отражена в уроках ниже.
Прямой CLI надёжнее обёртки: её фоновая машина глючит (молчаливая подмена
анализа Кодекса своим — issue #158, ломает слепое ревью; зависшие статусы),
и её --write-запуски режет классификатор авторежима. Далее $SCRATCH =
каталог скретчпада сессии из системного промпта (не /tmp: под Windows
/tmp Git Bash — ловушка).
Сквозные правила (любая ветка)
- Промпт — файлом, шум — в файлы. Промпт подавай через stdin (
-); stdout/stderr — редиректами в$SCRATCH. В контекст оркестратора читается ровно один файл — финальный отчёт из-o. Промпт собирай конкатенацией, а не переписывай: скелеты — в prompt-templates.md, Write'ом пиши только параметрический блок задачи; чужой контент (вердикты ревью, отчёты) в промпт попадает черезcat a.md b.md > prompt.md— дословно и без повторного прохода через твой контекст (ретро 2026-07-10: переписывание гоняло находки через оркестратора до шести раз). - Модель и глубину рассуждения задавай явно (
-m,-c 'model_reasoning_effort="..."'): дефолты берутся из~/.codex/config.tomlи плавают за последней интерактивной сессией пользователя. Для реализации и ревью — high; боевая модель —gpt-5.5(решение пользователя 2026-07-10). Модель из конфига НЕ наследовать: «сверить с конфигом» ≠ «взять из конфига» — туда утекают фронтирные модели интерактивных сессий (gpt-5.6-sol — фронтир уровня Fable, лимиты дорогие; казус — прогон тикета 02, 2026-07-10). Конфиг разошёлся с правилом — спроси пользователя. Оверрайды проверены под--strict-config. - Запуск — собственным фоновым Bash оркестратора (
run_in_background): фоновый процесс детачится, живёт сквозь ходы и будит оркестратора по выходу. Потолок 10 минут — только у фронтграундного Bash; фоновому таймаут не нужен,timeout NNNвокруг codex не ставь. - thread_id — в первой строке stdout-JSONL (
{"type":"thread.started",...}). Забирай сразу после старта: это страховка — если процесс умер или его прибил хост, тред жив:codex exec resume <id> «дай финальный отчёт». - resume — только по явному id. Безадресный resume цепляет последний тред сессии — при параллельных тредах подхватит чужой (например, ревью-тред вместо треда реализации).
- Вердикт без находок — проверь лог: работа или отписка. Дёшево:
grep -c '"command_execution"' stdout.jsonl(счётчик выполненных команд) или промежуточные сообщения —jq -r 'select(.item.type=="agent_message") | .item.text' stdout.jsonl. Одна строка APPROVED при пустом счётчике — отписка, перезапускай. - Кросс-платформенно. Рецепты — POSIX под Git Bash (эмпирика 2026-07-09:
codex,jq, редиректы под Windows работают).$SCRATCHзадавай forward-slash-путём (/c/Users/...), неC:\...; пути в аргументах кавычь; в выводах под Windows возможны\r. - Linux: sandbox может молча сломаться обновлением Codex. CLI 0.144.1
сменил backend на bwrap+userns; Ubuntu 24.04 с
apparmor_restrict_unprivileged_userns=1глушит любые команды до запуска (bwrap: loopback: Failed RTM_NEWADDR). Сигнатура: web_search работает, аpwdв sandbox — нет. Обход без sudouse_legacy_landlock=trueчинит только read-only (write-режим с ним паникует); полный фикс — AppArmor- профиль. Диагностика и рецепты — codex-sandbox-fix-handover-2026-07-10.md. Фикс целиком под sudo — сам не применяй: дай пользователю готовые команды из handover-файла (!-префикс в чат), после применения профиля напомни убратьuse_legacy_landlock. Перед первым прогоном конвейера на новой Linux-машине прогони диагностику из handover-файла превентивно. - Блок классификатора — решение пользователя. Если запуск не прошёл
permission-гейт, дай пользователю готовую команду с префиксом
!в чат или попроси сменить режим разрешений. Сработавший путь — прямой exec; companion с--writeблокируется и напрямую, и через раннера.
Ветка «исполнитель» (пишет код)
cat "$SCRATCH/task-prompt.md" | codex exec --json --strict-config \
-s workspace-write -m gpt-5.5 \
-c 'model_reasoning_effort="high"' \
-c 'approval_policy="on-request"' -c 'approvals_reviewer="auto_review"' \
-o "$SCRATCH/task-result.md" - \
> "$SCRATCH/task-stdout.jsonl" 2> "$SCRATCH/task-stderr.txt"
--strict-config— обязателен: опечатка в-c-ключе упадёт громко, а не проглотится молча (важно дляapprovals_reviewer— в--helpон не документирован, но strict-config его принимает; связка сapproval_policy="on-request"отработала боевой прогон 2026-07-09).- Право записи + авто-ревьюер аппрувов. Скоуп ограничивай промптом («коммит не делай», границы из файла задачи), не песочницей — жёсткий read-only лишает исполнителя тестов и поиска.
- Промпт: путь к файлу задачи; мандат — в этапе 3 SKILL.md.
- До старта сними baseline
git status --porcelain; после — сверь отчёт Кодекса с деревом: правок, которых он не называл, быть не должно.
Ветка «ревьюер» (свежий тред, мандат промптом)
Тот же вызов, что у исполнителя — запись + авто-аппрув, — но свежим
тредом, не тем, что писал код. Read-only-песочницей ревьюера не режь: она
отнимает у него руки — тесты, проверку гипотез, поиск, документацию,
первоисточники. Защита — мандат в промпте: «исходники и окружение не менять,
правок не вносить — только находки»; Codex держит его надёжно (практика
пользователя, 2026-07-10). Дешёвая подстраховка: git status --porcelain
до ревью и сверка после — дерево должно остаться нетронутым. Кавеат: сеть
под workspace-write по умолчанию закрыта; что поиск/документация проходят
эскалацией через авто-аппрув — рабочая гипотеза, боем не обкатана
(см. handoff 2026-07-10).
В промпте — формат «только индекс находок» (SEVERITY, файл:строка, суть,
как проверить), явная строка VERDICT и поощрение безопасных эмпирических
проверок вне репо (воспроизвести краевой случай, свериться с
первоисточником) — вердикт заметно твёрже (эмпирика 2026-07-09).
-o-файл вердикта — и есть обменный артефакт: на триаже он уходит в
fix-промпт конкатенацией, читай его один раз для синтеза и не пересказывай.
Ветка «триаж/починка» (тем же тредом, что писал код)
cat "$SCRATCH/fix-prompt.md" | codex exec resume --json <thread-id> \
-o "$SCRATCH/fix-result.md" - \
> "$SCRATCH/fix-stdout.jsonl" 2> "$SCRATCH/fix-stderr.txt"
resumeне принимает-s/-C— песочница и CWD наследуются от исходного треда (write-тред остаётся write); модель наследуется, но переопределяется-mпри нужде.- Промпт — индекс находок дословно + «закрой перечисленное, больше ничего не меняй; отклонение — только аргументированно».
Раннер-субагент: когда результат надо переварить
Для запуска раннер не нужен (см. сквозные правила) — он нужен, когда сырой
результат шумный и его стоит провалидировать или сжать до индекса, прежде
чем показывать оркестратору. Тогда раннеру (fast-worker; нет в
репозитории — general-purpose) отдай
готовую команду и правила разбора — не постановку задачи: тонкий агент,
получив постановку, реализует её сам (казус форвардера, lessons 2026-07-06).
Свой 10-минутный потолок на синхронный Bash раннер обходит отслеживаемым
ожиданием: команда запускается его собственным фоновым Bash, а ждёт он
процессом-ожидателем (until grep -q '<маркер>' лог; do sleep 30; done под
run_in_background) — харнесс переинвоцирует его по выходу процесса.
«Жду уведомления» без отслеживаемого процесса не будит никогда
(эмпирика 2026-07-07).
Companion-плагин (codex-companion.mjs в кэше плагинов) — только для
ручного интерактива (status, result <job-id>); конвейер ходит прямым
exec.